c# interface override language-features explicit-implementation

C#: propiedad que invalida especificando la interfaz explícitamente



interface override (1)

Al intentar anular la implementación de la interfaz explícita de la propiedad ICollection<T>.IsReadOnly de la clase Collection<T> , encontré algunos documentos que indican que las implementaciones de miembros de la interfaz explícita no se pueden anular porque no pueden tener modificadores como virtual o abstract . En MSDN incluso llegan a especificar cómo hacer que una implementación de miembro de interfaz explícita esté disponible para la herencia mediante la creación de otro miembro abstracto o virtual al que se llama la implementación de miembro de interfaz explícita. No hay problemas hasta ahora.

Pero luego me pregunto: ¿Por qué es posible en C # anular cualquier miembro de la interfaz implementado explícitamente simplemente especificando la interfaz explícitamente ?

Por ejemplo, supongamos que tengo una interfaz simple como esta, con una propiedad y un método:

public interface IMyInterface { bool AlwaysFalse { get; } bool IsTrue(bool value); }

Y una clase A que implementa la interfaz explícitamente, y tiene un método Test() que llama a su propia implementación de miembro de la interfaz.

public class A : IMyInterface { bool IMyInterface.AlwaysFalse { get { return false; } } bool IMyInterface.IsTrue(bool value) { return value; } public bool Test() { return ((IMyInterface)this).AlwaysFalse; } }

Como puede ver, ninguno de los cuatro miembros es virtual o abstracto, así que cuando defino una clase B como esta:

public class B : A { public bool AlwaysFalse { get { return true; } } public bool IsTrue(bool value) { return !value; } }

Entonces esperarías que una instancia de B convertida en A comporte como A Y lo hace:

A a = new A(); Console.WriteLine(((IMyInterface)a).AlwaysFalse); // False Console.WriteLine(((IMyInterface)a).IsTrue(false)); // False Console.WriteLine(a.Test()); // False A b = new B(); Console.WriteLine(((IMyInterface)b).AlwaysFalse); // False Console.WriteLine(((IMyInterface)b).IsTrue(false)); // False Console.WriteLine(b.Test()); // False

Ahora viene la trampa. Cree una clase C que es una copia exacta de B excepto por una cosa en la declaración de clase:

public class C : A, IMyInterface { /* ... same as B ... */ }

Ahora, una instancia de C , cuando se lanza a A , no se comporta como A sino como C :

A c = new C(); Console.WriteLine(((IMyInterface)c).AlwaysFalse); // True Console.WriteLine(((IMyInterface)c).IsTrue(false)); // True Console.WriteLine(c.Test()); // True

¡Incluso el método Test() ahora llama al método anulado en C ! ¿Por qué es esto?


Esto no tiene nada que ver con la implementación explícita de la interfaz; es simplemente una consecuencia de las reglas generales de herencia y asignación de interfaz: vería exactamente los mismos resultados si el tipo A proporcionara una implementación implícita, en lugar de explícita, de IMyInterface .

  • El tipo B hereda del tipo A Nada está anulado.
    B proporciona sus propios miembros AlwaysFalse e IsTrue pero no implementan IMyInterface ; La implementación de IMyInterface es proporcionada por los miembros heredados de A : cuando una instancia de tipo B se IMyInterface en IMyInterface , se comporta exactamente de la misma manera que en una instancia de tipo A porque A proporciona los miembros que implementan la interfaz.
  • El tipo C hereda del tipo A Una vez más, nada está anulado.
    C proporciona sus propios miembros AlwaysFalse e IsTrue , pero esta vez esos miembros implementan IMyInterface : cuando una instancia de tipo C se IMyInterface en IMyInterface , los miembros de C proporcionan la implementación de la interfaz en lugar de A

Debido a que el tipo A implementa IMyInterface explícitamente, el compilador no advierte que los miembros de B y C están ocultando a los miembros de A ; En efecto, esos miembros de A ya estaban ocultos debido a la implementación de la interfaz explícita.

Si cambió el tipo A para implementar IMyInterface implícitamente en lugar de explícitamente, entonces el compilador advertirá que los miembros de B y C están ocultando, no anulando, los miembros de A y que idealmente debería usar el new modificador cuando declare esos miembros en B y C

Aquí hay algunos bits relevantes de la especificación del lenguaje. (Secciones 20.4.2 y 20.4.4 en la especificación ECMA-334 ; secciones 13.4.4 y 13.4.6 en la especificación Microsoft C # 4 ).

20.4.2 Mapeo de la interfaz

La implementación de un IM miembro de interfaz particular, donde I es la interfaz en la que se declara el miembro M , se determina examinando cada clase o estructura S , comenzando con C y repitiendo para cada clase base sucesiva de C , hasta que se localice una coincidencia .

20.4.4 Reimplementación de la interfaz

Se permite que una clase que herede una implementación de interfaz vuelva a implementar la interfaz al incluirla en la lista de clases base. Una reimplementación de una interfaz sigue exactamente las mismas reglas de asignación de interfaz que una implementación inicial de una interfaz. Por lo tanto, la asignación de interfaz heredada no tiene ningún efecto en la asignación de interfaz establecida para la reimplementación de la interfaz.