Je comprends que les interfaces sont des contrats et toutes les modifications (même les ajouts) cassent tout code dépendant. Cependant, j'aurais juré avoir lu quelque chose en arrière que l'une des versions récentes de .NET (3, 3.5 ??) a ajouté un nouvel attribut qui pourrait être appliqué aux nouveaux membres de l'interface. Cet attribut autorisait le contrôle de version et/ou le rendu facultatif des membres. Cela aurait été quelque chose comme:
interface ITest
{
void MethodOne();
[InterfaceVersion(2)]
void MethodTwo();
}
J'ai regardé haut et bas pour cela, mais je n'arrive pas à le trouver. Je me demande si j'ai tout simplement mal compris ce que je pense avoir lu et il n'y a rien de tel. Quelqu'un a-t-il une idée?
Vous devez créer deux interfaces:
interface ITest
{
void MethodOne();
}
interface ITest2 : ITest
{
void MethodTwo();
}
Cela indiquerait également clairement quelles fonctionnalités nécessitent quelle version de vos interfaces, afin que vous n'ayez pas à vérifier si la classe implémentant l'interface implémente une seule méthode, ou les deux.
Je n'ai pas vu un tel attribut, mais je suppose que c'est possible. Cet article sur MSDN décrit la gestion des versions à l'aide des mots clés overrides
et new
.
En bref, C # est équipé de fonctionnalités de langage qui permettent aux classes dérivées d'évoluer tout en conservant la compatibilité. Cet exemple montre une relation purement base-dérivée, mais la base implémenterait en fait l'interface dont vous avez besoin de la version. Avoir une interface nécessite une autre interface (version précédente) couplée à cette méthode est également très utile.
Exemple de création d'une interface qui en nécessite une autre:
public interface IMyInterface
{
void FirstMethod();
}
public interface IMySecondInterface : IMyInterface
{
void SecondMethod();
}
Exemple d'utilisation de l'héritage pour maintenir la compatibilité:
public class MyBase
{
public virtual string Meth1()
{
return "MyBase-Meth1";
}
public virtual string Meth2()
{
return "MyBase-Meth2";
}
public virtual string Meth3()
{
return "MyBase-Meth3";
}
}
class MyDerived : MyBase
{
// Overrides the virtual method Meth1 using the override keyword:
public override string Meth1()
{
return "MyDerived-Meth1";
}
// Explicitly hide the virtual method Meth2 using the new
// keyword:
public new string Meth2()
{
return "MyDerived-Meth2";
}
// Because no keyword is specified in the following declaration
// a warning will be issued to alert the programmer that
// the method hides the inherited member MyBase.Meth3():
public string Meth3()
{
return "MyDerived-Meth3";
}
public static void Main()
{
MyDerived mD = new MyDerived();
MyBase mB = (MyBase) mD;
System.Console.WriteLine(mB.Meth1());
System.Console.WriteLine(mB.Meth2());
System.Console.WriteLine(mB.Meth3());
}
}
Pensez-vous peut-être à la nouvelle fonctionnalité "no pia" en C # 4? Autrement dit, nous vous permettons de "lier" uniquement les parties d'une interface que vous utilisez réellement à partir d'un PIA, puis vous pouvez ignorer l'envoi du PIA à vos clients. Si vous effectuez ensuite cette opération plusieurs fois dans plusieurs assemblys différents, le CLR fait le travail pour déterminer que toutes ces interfaces partielles liées sont logiquement du même type et les unifie. De cette façon, vous pouvez passer des objets qui implémentent chaque saveur de l'interface d'un assembly à un autre et tout fonctionne simplement. Cependant, les interfaces originales à partir desquelles les interfaces "no pia" sont créées doivent être les mêmes.
Je ne connais aucun attribut de ce type permettant à une implémentation d'interface d'être partiellement implémentée. Vous pouvez cependant contourner ce problème en utilisant une classe abstraite:
public abstract class Test
{
public abstract void MethodOne();
public virtual void MethodTwo() { }
}
Cela permettrait à l'utilisateur de décider s'il souhaite ou non remplacer MethodTwo lors de l'héritage de Test, tout en forçant le remplacement de MethodOne.
Il n'y a pas un tel attribut dans le framework .NET.
Mise à jour pour 2019: Si votre projet prend en charge C # 8.0, vous pouvez utiliser "implémentations d'interface par défaut = ", ce qui rend la méthode facultative à implémenter et retombe sur l'implémentation par défaut si vous choisissez de ne pas l'implémenter.
interface ITest
{
void MethodOne();
public void MethodTwo()
{
//Empty default implementation
}
}
J'étais récemment dans la situation où le manque dicté d'héritage multiple m'interdisait de transformer une interface existante en classe abstraite, et je me suis retrouvé avec une solution d'extension:
interface IFoo {
int RowCount();
}
static class _FooExtensions {
public static bool HasAnyRows (this IFoo foo) {
return foo.RowCount() > 0;
}
}
De cette façon, vous pouvez fournir une version par défaut au cas où votre méthode abstraite peut être définie en fonction des autres fonctions.
Vous avez peut-être lu quelque chose comme
interface ITest
{
void MethodOne();
[InterfaceVersion(2)]
void MethodTwo();
}
[AttributeUsage(AttributeTargets.All)]
public class InterfaceVersion : System.Attribute
{
public readonly int N;
public InterfaceVersion(int n)
{
this.N = n;
}
}
Mais je ne pense pas que cela pourrait rendre l'implémentation de MethodTwo
facultative.
MODIFIER:
Je viens de découvrir en exécutant le code qu'il ne rend vraiment pas l'implémentation de MethodTwo
facultative.