Ambigüedad del método de interfaz C#


Considere el siguiente ejemplo:

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 compile. Ambiguity between 'Percentage' property.
}

En el ejemplo anterior, hay ambigüedad entre la propiedad Percentage a llamar. Suponiendo que las interfaces IBase1 y IBase2 pueden no ser cambiadas, ¿cómo resolvería esta ambigüedad de la manera más limpia y preferida?

Actualización

Basado en las respuestas que estaba recibiendo por usar una implementación de interfaz explícita, quiero mencionar que si bien esto resuelve el problema, no lo resuelve en un ideal para mí porque uso mi AllYourBase objeto como un IAllYourBase la mayor parte del tiempo, nunca como un IBase1 o IBase2. Esto se debe principalmente a que IAllYourBase también tiene métodos de interfaz (no los detallé en mi fragmento de código anterior porque pensé que eran irrelevantes) que están implementados por AllYourBase y quiero acceder a ellos también. Lanzar hacia adelante y hacia atrás todo el tiempo se volverá muy tedioso y resultará en un código desordenado.

Probé una solución que implicaba definir la propiedad Percentage en IAllYourBase y no usar implementación de interfaz explícita, que parecía deshacerse del error del compilador al menos:

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

¿Es esta una solución válida?

Author: akjoshi, 2011-08-16

9 answers

Implementar explícitamente:

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

Si hace esto, por supuesto, puede tratar sus propiedades no ambiguas como siempre.

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

Sin embargo, si desea acceder al porcentaje de prop, esto no funcionará (Supongamos que lo hizo, ¿qué valor se esperaría a cambio?)

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

Debe especificar qué porcentaje devolver. Y esto se hace mediante la fundición a la interfaz correcta:

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

Como usted dice, puede redefinir las propiedades en el interfaz:

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; } }
}

Ahora ha resuelto la ambigüedad con nombres distintos.

 20
Author: Anders Forsgren,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-16 15:52:56

Implementar las interfaces explícitamente.

Una implementación de miembro de interfaz explícita es una declaración de método, propiedad, evento o indexador que hace referencia a un nombre de miembro de interfaz completo

Vea esta página de MSDN para un tutorial detallado.

interface AllYourBase : IBase1, IBase2
{
   int IBase1.Percentage { get; set; }
   int IBase2.Percentage { get; set; }
}
 10
Author: Donut,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-16 15:50:39

Suponiendo que desea que ambas propiedades accedan a la variable miembro percent , puede lograr esto utilizando una implementación de interfaz explícita y extendiendo la interfaz 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; }
   }
}

No es bonito, pero te dará el comportamiento que creo que buscas.

 8
Author: Sean,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-16 15:49:30

Implementarlo explícitamente y acceder a él:

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;
 7
Author: sll,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-16 15:53:45

Si realmente no necesita devolver valores diferentes para la propiedad Percentage, puede eliminar el error del compilador "derivando" de cada interfaz por separado, en lugar de la interfaz "maestra":

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

Por supuesto, si necesita valores separados, entonces tendrá que implementar explícitamente las interfaces, y acceder a la propiedad a través de una referencia apropiadamente escrita:

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; } }

}

Y el código:

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

    IBase2 b2 = ayb;
    double p2 = b2.Percentage;
}

Una consideración importante cuando implementar las interfaces explícitamente es que AllYourBase en sí ya no tiene una propiedad Percentage . Solo se puede acceder a él cuando se accede al objeto a través de una referencia escrita como una de las interfaces:

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

ACTUALIZAR : Mirando su edición, su solución está bien, asumiendo que no necesita un comportamiento diferente para IBase1 y IBase2. Su solución oculta esas propiedades, por lo que solo serán accesibles mediante la fundición del objeto a una de esas dos interfaces.

 7
Author: dlev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-16 16:05:31

Aunque ya has aceptado la respuesta. Yo diría que la implementación explícita pide demasiado trabajo redundante (Implementar la misma propiedad dos veces, en su caso) !

¿Qué tal una mayor segregación de la interfaz (Principio de Segregación 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; // Compiles now!!!
        }
    }
 4
Author: Manish Basantani,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-03-09 08:35:30

Si bien la implementación explícita que los chicos han mencionado es obviamente correcta, por favor considere seguir escenarios solo por el bien de la cordura (o hacerlo solo para una persona que lea su código en el futuro):

Si estos porcentajes tienen diferentes significados dependiendo de la interfaz, ¿por qué no darles algunos nombres significativos?

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

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

Y, por otro lado, si tienen el mismo significado, ¿por qué no tener solo un Porcentaje en una de las interfaces y derivar solo uno de otro?

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

interface IBase2 : IBase1
{
}

Sin embargo, si la última operación no es posible por la razón que sea, considere crear una interfaz base que contenga esa propiedad para ambos:

interface IBase0
{
    int Percentage { get; set; } 
}

interface IBase1 : IBase0
{ 
}

interface IBase2 : IBase0
{
}
 2
Author: Piotr Justyna,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-16 15:57:26
void Foo()
{
   IAllYourBase base = new AllYourBase();
   int percentage = base.Percentage; // Fails to compile. Ambiguity between 'Percentage' property.
}

En este ejemplo se utiliza la palabra clave base para un nombre de objeto y que puede estar causando el problema!!!

 1
Author: Bosak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-16 15:42:55

Puede definir la propiedad en la interfaz IAllYourBase.

Algo como esto :

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

Esto resolverá los problemas con la ambigüedad entre las propiedades. Y se puede mantener la estructura de las interfaces.

 1
Author: Jonas W,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-16 11:35:51