2010-05-31 9 views
16

Me he tropezado con esta "característica" de C# - la clase base que implementa métodos de interfaz no tiene que derivar de ella.¿Por qué una clase base en C# permite implementar un contrato de interfaz sin heredar de él?

Ejemplo:

public interface IContract 
{ 
    void Func(); 
} 

// Note that Base does **not** derive from IContract 
public abstract class Base 
{ 
    public void Func() 
    { 
     Console.WriteLine("Base.Func"); 
    } 
} 

// Note that Derived does *not* provide implementation for IContract 
public class Derived : Base, IContract 
{ 
} 

Lo que pasa es que Derived recoge-mágicamente un método público, Base.Func, y decide que pondrá en práctica IContract.Func.

¿Cuál es el motivo de esta magia?

En mi humilde opinión: esta característica de "casi implementación" es muy poco intuitiva y hace que la inspección del código sea mucho más difícil. ¿Qué piensas?

+1

Creo que puede evitar el efecto secundario con la implementación explícita de la interfaz en la clase derivada. – kenny

+3

Esto tiene sentido para mí. Cuando Derived hereda IContract, lo que eso significa es que proporciona Func(), que lo hace. Que lo haga a través de la herencia es meramente incidental. Tenga en cuenta que Base * no * implementa IContract: si intentaba convertir una Base en IContract, fallaría. – allonym

Respuesta

28

La razón es que su comentario es simplemente incorrecta:

// Tenga en cuenta que derivó qué no proporcionar implementación para IContract

seguro que lo hace. Sigue la lógica.

  • Derived se requiere para proporcionar un miembro público correspondiente a cada miembro de IContract.
  • Todos los miembros heredables de una clase base también son miembros de una clase derivada; esa es la definición de la herencia.
  • Por lo tanto, Derived proporciona una implementación para IContract; su miembro heredado es un miembro que cumple con el requisito
  • Por lo tanto, no hay ningún error.

esta característica es muy poco intuitivo y crea código de inspección mucho más difícil. ¿Qué piensas?

Creo que no deberías usar la función si no te gusta. Si le resulta confuso y extraño leer el código que usa esta función, anime a sus compañeros de trabajo que usan esta función a dejar de hacerlo.

¿En qué se diferencia esta función de cualquier otra característica en la que se utiliza un método de una clase base de una clase derivada? Hay varias formas diferentes en que un método de una clase base puede usarse o mencionarse en una clase derivada: llamadas a métodos, anulaciones, conversiones de grupos de métodos, etc.

Además, esto es relativamente simple, un caso sencillo. Si realmente quiere quejarse sobre la semántica de la interfaz confusa en C#, me gustaría gastar mi tiempo quejándose de semántica de reimplementación de la interfaz. Esa es la que realmente parece hornear fideos de la gente. Siempre tengo que buscar esa cosa en la especificación para asegurarme de obtener la semántica correcta.

+0

¡Gracias por la respuesta detallada! Yo * do * entiendo * cómo * funciona, pero en mi humilde opinión la forma en que funciona es contrario a la intuición por varias razones: (1) uno lee primero la etiqueta de precio y luego paga, de la misma manera que primero * declara * y solo * después de * * implementa * smth. (2) Derived es un IContract, no lo implementa, pero usa Base para implementar IContract, que no es un IContract en sí (3) Al leer el código para Base, no hay forma de decidir si Base.Func está relacionado con IContract .Func (la relación declarar/implementar). (4) Cambiar Base.Func producirá un error de compilación, que ni siquiera mencionará Base – etarassov

+0

"Creo que no debería usar la función si no le gusta". - 100% de acuerdo. Desafortunadamente, otras personas podrían usarlo (o la base de código existente) lo que hará que el código sea difícil de leer/comprender. El verdadero gran lenguaje guía al desarrollador a escribir un buen código (lo ideal sería forzarlo). C# (un lenguaje muy agradable) solo se beneficiaría si se elimina esta característica. Pero sería realmente difícil y doloroso ... Exactamente como con los cierres dentro de un foreach usando la variable de bucle. De todos modos, ¡gracias por el enlace a la "semántica de reimplementación de la interfaz"! – etarassov

10

¿Por qué crees que esto es extraño y antinatural? Cada miembro público de la clase base también es un miembro público de la clase derivada. Entonces no hay contradicción aquí. De todos modos, puedes implementar la interfaz explícitamente si quieres.

+1

En mi humilde opinión, este tipo de característica agrega complejidad innecesaria al idioma. La manera simple y natural sería derivar la Base de IContract para indicar explícitamente que la implementa. Los seres humanos son seres simples: no somos capaces de analizar instantáneamente todo el código y hacer una referencia cruzada del gráfico de herencia para comprender que IContract.Func y Base.Func están relacionados (renombrar IContract.Func uno tendrá que * cambiar * manualmente la Base .Func). Se vuelve aún más complicado cuando las tres clases residen en diferentes proyectos/soluciones. – etarassov

+0

En resumen: esta característica * es mala * porque la * legibilidad del código sufre mucho *. Un programador (posiblemente) tiene que pasar por cada clase base para encontrar una implementación de interfaz, incluso a través de las clases base que no están relacionadas con la interfaz. – etarassov

+1

ok Trataré de explicar: la situación que intentas describir es rara y antinatural. Especialmente este: "Se vuelve aún más complicado cuando las tres clases residen en diferentes proyectos/soluciones". Una vez más, puede implementar la interfaz explícitamente para evitar cualquier collizion con los métodos de clase base. – Voice

Cuestiones relacionadas