Ambiguïté de la méthode d’interface C #

Prenons l’exemple suivant:

interface IBase1 { int Percentage { get; set; } } interface IBase2 { int Percentage { get; set; } } interface IAllYourBase : IBase1, IBase2 { } class AllYourBase : IAllYourBase { int percentage; int Percentage { get { return percentage; } set { percentage = value; } } } void Foo() { IAllYourBase iayb = new AllYourBase(); int percentage = iayb.Percentage; // Fails to comstack. Ambiguity between 'Percentage' property. } 

Dans l’exemple ci-dessus, il y a une ambiguïté entre quelle propriété de Percentage appeler. En supposant que les interfaces IBase1 et IBase2 ne puissent pas être modifiées, comment puis-je résoudre cette ambiguïté de la manière la plus propre et la plus préférée?

Mettre à jour

En me basant sur les réponses que je recevais pour utiliser une implémentation d’interface explicite, je tiens à mentionner que si cela résout le problème, il ne le résout pas de façon idéale car j’utilise mon object AllYourBase comme un IAllYourBase plupart du temps, jamais. comme IBase1 ou IBase2 . Ceci est principalement dû au IAllYourBase que IAllYourBase également des méthodes d’interface (j’ai omis de détailler celles de mon extrait de code ci-dessus parce que je pensais qu’elles n’étaient pas pertinentes) qui sont implémentées par AllYourBase et je souhaite également y accéder. Les allers et retours tout le temps deviennent très fastidieux et génèrent du code compliqué.

J’ai essayé une solution qui impliquait de définir la propriété Percentage dans IAllYourBase et de ne pas utiliser l’implémentation d’interface explicite, ce qui semblait éliminer au moins l’erreur de compilation:

 class IAllYourBase : IBase1, IBase2 { int Percentage { get; set; } } 

Est-ce une solution valable?

Implémenter explicitement:

 public class AllYourBase : IBase1, IBase2 { int IBase1.Percentage { get{ return 12; } } int IBase2.Percentage { get{ return 34; } } } 

Si vous faites cela, vous pouvez bien sûr traiter vos propriétés non ambigües comme d’habitude.

 IAllYourBase ab = new AllYourBase(); ab.SomeValue = 1234; 

Cependant, si vous voulez accéder au pourcentage d’hélice, cela ne fonctionnera pas (supposons que c’est le cas, quelle valeur serait attendue en retour?)

 int percent = ab.Percentage; // Will not work. 

vous devez spécifier le pourcentage à renvoyer. Et cela se fait en convertissant l’interface correcte:

 int b1Percent = ((IBase1)ab).Percentage; 

Comme vous le dites, vous pouvez redéfinir les propriétés dans l’interface:

 interface IAllYourBase : IBase1, IBase2 { int B1Percentage{ get; } int B2Percentage{ get; } } class AllYourBase : IAllYourBase { public int B1Percentage{ get{ return 12; } } public int B2Percentage{ get{ return 34; } } IBase1.Percentage { get { return B1Percentage; } } IBase2.Percentage { get { return B2Percentage; } } } 

Maintenant, vous avez résolu l’ambiguïté par noms distincts.

Implémentez les interfaces explicitement.

Une implémentation de membre d’interface explicite est une déclaration de méthode, de propriété, d’événement ou d’indexeur qui fait référence à un nom de membre d’interface qualifié complet.

Voir cette page MSDN pour un tutoriel détaillé.

 interface AllYourBase : IBase1, IBase2 { int IBase1.Percentage { get; set; } int IBase2.Percentage { get; set; } } 

En supposant que vous souhaitiez que les deux propriétés accèdent à la variable % member, vous pouvez le faire en utilisant une implémentation d’interface explicite et en étendant l’interface IAllYouBase :

 interface IAllYourBase : IBase1, IBase2 { new int Percentage { get; set; } } class AllYourBase : IAllYourBase { int percentage; public int Percentage { get { return percentage; } set { percentage = value; } } int IBase1.Percentage { get { return percentage; } set { percentage = value; } } int IBase2.Percentage { get { return percentage; } set { percentage = value; } } } 

Ce n’est pas joli, mais cela vous donnera le comportement que je pense que vous recherchez.

Implémenter explicitement et y accéder:

 interface IAllYourBase : IBase1, IBase2 { } public class AllYourBase : IAllYourBase { int IBase1.Percentage { get{ return 12; } } int IBase2.Percentage { get{ return 34; } } } IAllYourBase base = new AllYourBase(); int percentageBase1 = (base as IBase1).Percentage; int percentageBase2 = (base as IBase2).Percentage; 

Si vous n’avez pas réellement besoin de renvoyer des valeurs différentes pour la propriété Percentage , vous pouvez éliminer l’erreur du compilateur en “dérivant” de chaque interface séparément, plutôt que l’interface “maître”:

 public class AllYourBase : IBase1, IBase2 { // No need to explicitly implement if the value can be the same public double Percentage { get { return 12d; } } } 

Bien sûr, si vous avez besoin de valeurs séparées, vous devrez implémenter explicitement les interfaces et accéder à la propriété via une référence correctement typée:

 public class AllYourBase : IBase1, IBase2 { // No need to explicitly implement if the value can be the same public double IBase1.Percentage { get { return 12d; } } public double IBase2.Percentage { get { return 34d; } } } 

Et le code:

 public void SomeMethod() { AllYourBase ayb = new AllYourBase(); IBase1 b1 = ayb double p1 = b1.Percentage; IBase2 b2 = ayb; double p2 = b2.Percentage; } 

Lors de la mise en œuvre explicite des interfaces, il est important de prendre en compte le fait que AllYourBase n’a plus de propriété Percentage . Il n’est accessible que lorsque l’object est accessible via une référence typée comme l’une des interfaces:

 public void SomeMethod() { AllYourBase ayb = new AllYourBase(); double d = ayb.Percentage; // This is not legal } 

MISE À JOUR : En IBase1 votre édition, votre solution est IBase1 , à condition que vous n’ayez pas besoin d’un comportement différent pour IBase1 et IBase2 . Votre solution masque ces propriétés, elles ne seront donc accessibles qu’en convertissant l’object vers l’une de ces deux interfaces.

Bien que vous ayez déjà accepté la réponse. Je dirais que l’implémentation explicite demande trop de travail redondant (implémentant deux fois la même propriété, dans votre cas)!

Que diriez-vous d’une séparation plus poussée de l’interface (principe de ségrégation Inteface)?

  internal interface IPercentage { int Percentage { get; set; } } internal interface IBase1 : IPercentage { } internal interface IBase2 : IPercentage { } internal interface IAllYourBase : IBase1, IBase2 { } internal class AllYourBase : IAllYourBase { private int percentage; public int Percentage { get { return percentage; } set { percentage = value; } } void Foo() { IAllYourBase iayb = new AllYourBase(); int percentage = iayb.Percentage; // Comstacks now!!! } } 

Bien que la mise en œuvre explicite que les gars aient mentionnée soit évidemment correcte, veuillez envisager les scénarios suivants pour des raisons de santé mentale (ou faites-le uniquement pour une personne lisant votre code ultérieurement):

Si ces pourcentages ont des significations différentes selon l’interface, pourquoi ne pas leur atsortingbuer des noms significatifs?

 interface IBase1 { int PercentageBase1 { get; set; } } interface IBase2 { int PercentageBase2 { get; set; } } 

Et, d’autre part, si elles ont le même sens, pourquoi ne pas avoir juste un pourcentage dans l’une des interfaces et en tirer un à partir de l’autre?

 interface IBase1 { int Percentage { get; set; } } interface IBase2 : IBase1 { } 

Toutefois, si la dernière opération n’est pas possible pour une raison quelconque, envisagez de créer une interface de base contenant cette propriété pour les deux:

 interface IBase0 { int Percentage { get; set; } } interface IBase1 : IBase0 { } interface IBase2 : IBase0 { } 
 void Foo() { IAllYourBase base = new AllYourBase(); int percentage = base.Percentage; // Fails to comstack. Ambiguity between 'Percentage' property. } 

Dans cet exemple, vous utilisez le mot clé base pour un nom d’object et cela peut être à l’origine du problème !!!

Vous pouvez définir la propriété dans l’interface IAllYourBase .

Quelque chose comme ça :

 interface IAllYourBase : IBase1, IBase2 { new int Percentage { get; set; } } 

Cela résoudra les problèmes d’ambiguïté entre les propriétés. Et vous pouvez conserver la structure des interfaces.