2010-09-01 9 views
96

Los métodos C# en las interfaces se declaran sin utilizar la palabra clave virtual, y se invalidan en la clase derivada sin utilizar la palabra clave override.¿Por qué los métodos de interfaz C# no se declaran abstractos o virtuales?

¿Hay alguna razón para esto? Supongo que es solo una conveniencia de lenguaje, y obviamente el CLR sabe cómo manejar esto bajo la tapa (los métodos no son virtuales por defecto), pero ¿hay otras razones técnicas?

Aquí es la IL que una clase derivada genera:

class Example : IDisposable { 
    public void Dispose() { } 
} 

.method public hidebysig newslot virtual final 
     instance void Dispose() cil managed 
{ 
    // Code size  2 (0x2) 
    .maxstack 8 
    IL_0000: nop 
    IL_0001: ret 
} // end of method Example::Dispose 

en cuenta que el método se declara virtualfinal en el IL.

+2

porque están declarados como interfaz –

+2

@Muad: Ver mi edición. –

Respuesta

128

Para la interfaz, la adición de la abstract, o incluso las palabras clave public sería redundante, por lo que los omite:

interface MyInterface { 
    void Method(); 
} 

En el CIL, el método está marcado virtual y abstract.

(Tenga en cuenta que Java permite que los miembros de la interfaz se declaren public abstract).

Para la clase de aplicación, hay algunas opciones:

no reemplazable: En C# la clase no declarar el método como virtual. Eso significa que no puede ser anulado en una clase derivada (solo oculta). En el CIL, el método sigue siendo virtual (pero sellado) porque debe soportar el polimorfismo en relación con el tipo de interfaz.

class MyClass : MyInterface { 
    public void Method() {} 
} 

Overridable: Tanto en C# y en el CIL el método es virtual. Participa en el envío polimórfico y puede anularse.

class MyClass : MyInterface { 
    public virtual void Method() {} 
} 

explícita: Esta es una manera para que una clase para implementar una interfaz, pero no proporciona los métodos de interfaz en la interfaz pública de la propia clase. En el CIL, el método será private (!) Pero aún así se podrá llamar desde fuera de la clase desde una referencia al tipo de interfaz correspondiente. Las implementaciones explícitas tampoco son invalidables. Esto es posible porque hay una directiva CIL (.override) que enlazará el método privado con el método de interfaz correspondiente que está implementando.

[C#]

class MyClass : MyInterface { 
    void MyInterface.Method() {} 
} 

[CIL]

.method private hidebysig newslot virtual final instance void MyInterface.Method() cil managed 
{ 
    .override MyInterface::Method 
} 

En VB.NET, incluso se puede alias el nombre de método de interfaz en la clase que implementa.

[VB.NET]

Public Class MyClass 
    Implements MyInterface 
    Public Sub AliasedMethod() Implements MyInterface.Method 
    End Sub 
End Class 

[CIL]

.method public newslot virtual final instance void AliasedMethod() cil managed 
{ 
    .override MyInterface::Method 
} 

Ahora, considere este caso raro:

interface MyInterface { 
    void Method(); 
} 
class Base { 
    public void Method(); 
} 
class Derived : Base, MyInterface { } 

Si BaseDerived y se declaran en el mismo conjunto , el compilador hará Base::Method virtual y sellado (en el CIL), aunque Base no implementa la interfaz.

Si BaseDerived y se encuentran en diferentes montajes, al compilar el conjunto Derived, el compilador no cambia el otro conjunto, por lo que introducirá un miembro de Derived que será una implementación explícita para MyInterface::Method que se acaba de delegar la llama al Base::Method.

Como puede ver, cada implementación del método interfaz debe apoyar el comportamiento polimórfico, y por lo tanto debe ser marcada virtual en el CIL, incluso si el compilador debe pasar por el aro para hacerlo.

0

Ellos no son virtuales (en términos de lo que pensamos de ellos, si no en términos de implementación subyacente como (sellado virtual) - bueno leer las otras respuestas aquí y aprender algo a mí mismo :-)

No anulan nada: no hay implementación en la interfaz.

Todo lo que hace la interfaz es proporcionar un "contrato" al que la clase debe adherirse, un patrón, si lo desea, para que las personas que llaman sepan llamar al objeto incluso si nunca antes lo habían visto.

Depende de la clase el implementar el método de interfaz como lo hará, dentro de los límites del contrato, virtual o "no virtual" (sellado virtual como resulta).

+0

todos en este hilo saben para qué son las interfaces. La pregunta es extremadamente específica: el IL generado * es * virtual para el método de interfaz y no virtual para un método que no es de interfaz. –

+5

Sí, es muy fácil criticar una respuesta después de que se haya editado la pregunta, ¿no es así? –

66

Citando Jeffrey Ritcher de CLR a través de CSharp tercera edición aquí

El CLR requiere que la interfaz métodos pueden marcar como virtual. 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 la interfaz método. 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 está sellado un método de interfaz, una clase derivada no puede anular el método . Sin embargo, una clase derivada puede re-heredar la misma interfaz y puede proporcionar su propia implementación para los métodos de la interfaz .

+24

La cita no dice _por qué_ se requiere que una implementación de método de interfaz se marque como virtual. Es porque es polimórfico con respecto al tipo de interfaz, por lo que _necesita_ una ranura en la tabla _virtual_ para permitir el envío de métodos virtuales. –

+1

No tengo permitido marcar explícitamente el método de interfaz como virtual, y obtengo el error "error CS0106: El modificador 'virtual' no es válido para este elemento". Probado usando v2.0.50727 (la versión más antigua en mi PC). – ccppjava

+3

@ccppjava Del comentario de Jorado a continuación, marque el miembro de la clase que está implementando la interfaz virtual para permitir que las subclases anulen la clase. –

4

En la mayoría de los demás entornos de código compilado, las interfaces se implementan como vtables: una lista de punteros a los cuerpos de método. Normalmente, una clase que implementa múltiples interfaces tendrá en algún lugar de sus metadatos generados por el compilador interno una lista de tablas de interfaz, una tabla por interfaz (para que el orden del método se conserve). Así es como las interfaces COM también se implementan típicamente.

En .NET, sin embargo, las interfaces no se implementan como tablas distintas para cada clase. Los métodos de interfaz se indexan a través de una tabla de métodos de interfaz global de la que forman parte todas las interfaces. Por lo tanto, no es necesario declarar un método virtual para que ese método implemente un método de interfaz: la tabla de método de interfaz global puede apuntar directamente a la dirección de código del método de la clase.

Tampoco se requiere declarar un método virtual para implementar una interfaz en otros lenguajes, incluso en plataformas que no sean CLR. El lenguaje Delphi en Win32 es un ejemplo.

10

Sí, los métodos de implementación de la interfaz son virtuales en lo que respecta al tiempo de ejecución. Es un detalle de implementación, hace que las interfaces funcionen. Los métodos virtuales obtienen ranuras en la clase 'v-table', cada ranura tiene un puntero a uno de los métodos virtuales. Al convertir un objeto en un tipo de interfaz se genera un puntero a la sección de la tabla que implementa los métodos de interfaz. El código de cliente que utiliza la referencia de interfaz ahora ve el puntero del primer método de interfaz en el desplazamiento 0 desde el puntero de la interfaz, etcétera.

Lo que menosprecié en mi respuesta original es la importancia del atributo final. Impide que una clase derivada anule el método virtual. Una clase derivada debe volver a implementar la interfaz, los métodos de implementación sombra los métodos de clase base. Lo cual es suficiente para implementar el contrato de lenguaje C# que dice que el método de implementación no es virtual.

Si declara que el método Dispose() en la clase Example es virtual, verá que se elimina el atributo final. Ahora permitiendo que una clase derivada lo anule.

0

<broma> Esto es algo que quizás quiera preguntarle a Anders Hejlsberg y al resto del equipo de diseño de C#. </broma >

interfaces son un concepto más abstracto que las clases, cuando se declara una clase que implementa una interfaz, que acaba de decir "la clase debe tener estos métodos particulares de la interfaz, y no importa wheter estática, virtual, no virtual, invalidado, siempre que tenga la misma ID y los mismos parámetros de tipo ".

Otros lenguajes que admiten interfaces como Object Pascal ("Delphi") y Objective-C (Mac) no requieren que los métodos de interfaz se marquen como virtuales y tampoco virtuales.

embargo, puede que tengas razón, creo que puede ser una buena idea tener un atributo específico "virtual"/"override" en las interfaces, en caso de que quiera restringir los métodos de clases que implementan una interfaz en particular. Pero eso también significa tener una palabra clave "no virtual", "dontcareifvirtualornot", para ambas interfaces.

Entiendo su pregunta, porque veo algo similar en Java, cuando un método de clase tiene que usar "@virtual" o "@override" para asegurarse de que un método sea virtual.

Cuestiones relacionadas