llamadas virtuales en implementaciones de interfaz anuladas

Si tengo dos clases que implementan una interfaz, pero también heredan, ¿debo hacer que la función sea virtual? por ejemplo dado:

interface IDoSomething
{
    void DoSomething();
}

class A : IDoSomething
{
    public void DoSomething()
    {
        //do A
    }
}

class B : A
{
    public new void DoSomething()
    {
        //do B
    }
}

¿El siguiente código haría A o B?

IDoSomething doer = new B();
doer.DoSomething(); //do A or do B?

Me estoy confundiendo porque tengo la impresión de que todas las llamadas de interfaz son efectivamente virtuales, pero obviamente estoy usando el nuevo operador para ocultar la definición base.

preguntado el 12 de junio de 12 a las 12:06

¿La pregunta es A o B, o el porqué ¿A y no B? Porque la primera la puedes responder tú mismo con bastante facilidad. -

Sí, más por qué, lo siento! Editará. -

4 Respuestas

Aquí es la explicación. Ya disponible en los foros de stackoverflow.

Citando a Jeffrey Ritcher de CLR a través de CSharp 3rd Edition aquí

El CLR requiere que los métodos de interfaz se marquen como virtuales. Si no marca explícitamente el método como virtual en su código fuente, el compilador marca el método como virtual y sellado; esto evita que una clase derivada anule el método de interfaz. Si marca explícitamente el método como virtual, el compilador marca el método como virtual (y lo deja sin sellar); esto permite que una clase derivada anule el método de interfaz. Si un método de interfaz está sellado, una clase derivada no puede anular el método. Sin embargo, una clase derivada puede volver a heredar la misma interfaz y puede proporcionar su propia implementación para los métodos de la interfaz.

contestado el 23 de mayo de 17 a las 13:05

Exactamente la información que buscaba, gracias! Incluso iba a preguntar sobre volver a heredar la interfaz. - GazTheDestroyer

class A : IDoSomething
{
    public virtual void DoSomething()
    {
        //do A
    }
}

class B : A
{
    public override void DoSomething()
    {
        //do B
    }
}

Respondido el 12 de junio de 12 a las 12:06

yo prefiero leppiela solución de . Si esa no es una opción:

class A : IDoSomething
{
    void IDoSomething.DoSomething()
    {
        //do A
    }
}

class B : A
{
    void IDoSomething.DoSomething()
    {
        //do B
    }
}

Pero tenga en cuenta que esto ocultará la implementación, por lo que no puede hacer ((A)doer).DoSomething().

Si no puede cambiar la clase A a ninguna de estas soluciones, no creo que haya una forma segura de anularla en todos los casos. Podrías implementar explícitamente la interfaz y hacer una public new método en B. De esa manera, si se conoce estáticamente como un IDoSomething o como B utilizará la implementación de B, pero si se conoce como A todavía usará Aimplementación.

Respondido el 12 de junio de 12 a las 12:06

Aunque C# y .net permiten que las clases derivadas vuelvan a implementar métodos de interfaz, a menudo es mejor que la clase base use un método virtual para implementar la interfaz y que la clase derivada anule ese método, en cualquier situación en la que una clase derivada pueda desea aumentar, en lugar de reemplazar por completo, la implementación de la clase base. En algunos lenguajes como vb.net, esto se puede hacer directamente sin importar si una clase expone un miembro público con el mismo nombre y firma que el miembro de la interfaz que se está implementando. En otros lenguajes como C#, un método público que implementa una interfaz se puede marcar como no sellado y virtual (permitiendo que una clase derivada lo anule y tenga esa llamada de anulación base.Member(params) pero una implementación de interfaz explícita no puede. En tales idiomas, lo mejor que se puede hacer es algo como:

clase MiClase : MiInterfaz { void MiInterfaz.HacerAlgo(int param) { hacerAlgo(param); } protected virtual void hacerAlgo(int param) { ... } } class MyClass2 : MyClass { protected override void hacerAlgo(int param) { ... base.hacerAlgo(param); ... } }

En algunos casos, hacer que la implementación de la interfaz ajuste una llamada virtual puede ser ventajoso, ya que permite que la clase base se asegure de que sucedan ciertas cosas antes o después de la función anulada. Por ejemplo, una implementación de interfaz no virtual de Dispose podría envolver un método Dispose virtual:

  int Disposición de bandera; // System.Boolean no funciona con Interlocked.Exchange void IDisposable.Dispose() { if (Threading.Interlocked.CompareExchange(DisposingFlag, 1, 0) == 0) { Dispose(true); Disposición de bandera = 2; Threading.Thread.MemoryBarrier(); GC.SuprimirFinalizar(esto); } } public bool Disposed { get {return (DisposingFlag != 0);} } public bool FullyDisposed { get {return (DisposingFlag > 1);} }

Esto (a diferencia del contenedor predeterminado de Microsoft) garantizará que Dispose solo se llama una vez, incluso si varios subprocesos intentan llamarlo simultáneamente. Además, hace un Disposed propiedad disponible. Usando el contenedor de Microsoft, cada clase derivada que quiere un Disposed bandera tendría que definir la suya propia; incluso si la clase base Disposed flag fuera protegida o pública, no sería seguro usarla porque no se establecería hasta después de que las clases derivadas ya hayan comenzado la limpieza. Ajuste DisposingFlag dentro de la envoltura evita ese problema.

Respondido el 12 de junio de 12 a las 16:06

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.