2011-01-27 7 views
66

List declaración de MSDN:¿Por qué (realmente lo hace?) Lista <T> implementar todas estas interfaces, no solo IList <T>?

public class List<T> : IList<T>, ICollection<T>, 
IEnumerable<T>, IList, ICollection, IEnumerable 

reflector da imágenes similares. ¿Realmente implementa List todos estos (si es así, por qué)? He comprobado:

interface I1 {} 
    interface I2 : I1 {} 
    interface I3 : I2 {} 

    class A : I3 {} 
    class B : I3, I2, I1 {} 

    static void Main(string[] args) 
    { 
     var a = new A(); 
     var a1 = (I1)a; 
     var a2 = (I2)a; 
     var a3 = (I3)a; 

     var b = new B(); 
     var b1 = (I1) b; 
     var b2 = (I2)b; 
     var b3 = (I3)b; 
    } 

compila.

[Actualizado]:

Guys, según tengo entendido, todas las respuestas que se quedan:

class Program 
{ 

    interface I1 {} 
    interface I2 : I1 {} 
    interface I3 : I2 {} 

    class A : I3 {} 
    class B : I3, I2, I1 {} 

    static void I1M(I1 i1) {} 
    static void I2M(I2 i2) {} 
    static void I3M(I3 i3) {} 

    static void Main(string[] args) 
    { 
     var a = new A(); 
     I1M(a); 
     I2M(a); 
     I3M(a); 

     var b = new B(); 
     I1M(b); 
     I2M(b); 
     I3M(b); 

     Console.ReadLine(); 
    } 
} 

daría error, pero puede compilar y ejecutar sin ningún error. ¿Por qué?

+2

Sí La lista implementa todas esas interfaces, ¿por qué ?, porque todas son relevantes para una colección de listas. ¿Para qué es tu código? ¿Es esa una pregunta diferente? –

+0

Su código de ejemplo no tiene nada que ver con la clase '' Lista ... – BoltClock

+0

creo que está preguntando por qué 'Lista pública de clase : IList , ICollection , IEnumerable , etc.' en lugar de simplemente' Lista public class : IList '. Tenga en cuenta que 'ICollection' y' IList' (los no genéricos) no se heredan de 'IList '. – Rup

Respuesta

129

ACTUALIZACIÓN: Esta pregunta fue la base de my blog entry for Monday April 4th 2011. Gracias por la gran pregunta.

Déjenme desglosarlo en muchas preguntas más pequeñas.

¿Realmente implementa List<T> todas esas interfaces?

Sí.

¿Por qué?

Porque cuando una interfaz (por ejemplo, IList<T>) hereda de una interfaz (por ejemplo IEnumerable<T>) entonces se requieren ejecutores de la interfaz más derivado para también implementar la interfaz menos derivada. Eso es lo que significa herencia de interfaz; si cumple el contrato del tipo más derivado, se le exige que también cumpla el contrato del tipo menos derivado.

Entonces, ¿se requiere una clase para implementar todos los métodos de todas las interfaces en el cierre transitivo de sus interfaces base?

Exactamente.

¿Es una clase que implementa una interfaz más derivada también necesaria para indicar en su lista de tipos básicos que está implementando todas esas interfaces menos derivadas?

es la clase requerida para indicar que NO?

Así que es opcional si las interfaces implementadas menos derivados se indican en la lista de tipos de base?

Sí.

¿Siempre?

Casi siempre:

interface I1 {} 
interface I2 : I1 {} 
interface I3 : I2 {} 

Es opcional si I3 afirma que hereda de I1.

class B : I3 {} 

implementadores de I3 están obligados a poner en práctica I2 e I1, pero no están obligados a declarar explícitamente que lo están haciendo. Es opcional

class D : B {} 

Las clases derivadas no están obligados a volver a exponer que implementan una interfaz de su clase base, pero se les permite hacerlo. (Este caso es especial; ver más abajo para más detalles).

class C<T> where T : I3 
{ 
    public virtual void M<U>() where U : I3 {} 
} 

argumentos de tipo correspondiente a T y T son necesarios para implementar I2 e I1, pero es opcional para las restricciones en T o T para afirmar que.

Siempre es opcional para volver a exponer cualquier interfaz de base en una clase parcial:

partial class E : I3 {} 
partial class E {} 

está permitida la segunda mitad de E para indicar que implementa I3 o I2 o I1, pero no es necesario hacer asi que.

OK, lo entiendo; es opcional ¿Por qué alguien declararía innecesariamente una interfaz base?

Tal vez porque creen que hacerlo hace que el código sea más fácil de entender y más autodocumentado.

O, tal vez el desarrollador ha escrito el código como

interface I1 {} 
interface I2 {} 
interface I3 : I1, I2 {} 

y se dio cuenta, oh, espera un minuto, I2 debe heredar de I1.¿Por qué hacer esa edición requiere que el desarrollador regrese y cambie la declaración de I3 a no que contenga mención explícita de I1? No veo ninguna razón para obligar a los desarrolladores a eliminar la información redundante.

Aparte de ser más fácil de leer y entender, ¿hay alguna diferencia técnica entre indicando una interfaz explícitamente en la lista tipo de base y dejándolo sin especificar pero implícita?

Normalmente no, pero puede haber una diferencia sutil en un caso. Supongamos que tiene una clase derivada D cuya clase base B ha implementado algunas interfaces. D implementa automáticamente esas interfaces a través de B. Si vuelve a establecer las interfaces en la lista de clases base de D, el compilador de C# realizará una nueva implementación de la interfaz . Los detalles son un poco sutiles; Si le interesa cómo funciona esto, le recomiendo una lectura cuidadosa de la sección 13.4.6 de la especificación C# 4.

¿El código fuente List<T> en realidad indica todas esas interfaces?

No. El código fuente real dice

public class List<T> : IList<T>, System.Collections.IList 

¿Por qué MSDN tienen la lista completa de interfaz pero el código fuente real no?

Porque MSDN es la documentación; se supone que debe darle tanta información como desee. Es mucho más claro que la documentación esté completa en un solo lugar que hacer que busque entre diez páginas diferentes para descubrir cuál es el conjunto de interfaz completo.

¿Por qué Reflector muestra toda la lista?

Reflector solo tiene metadatos para trabajar. Como poner en la lista completa es opcional, Reflector no tiene idea de si el código fuente original contiene la lista completa o no. Es mejor equivocarse al lado de más información. Una vez más, Reflector intenta ayudarlo mostrándole más información en lugar de ocultar la información que pueda necesitar.

PRIMA PREGUNTA: ¿Por qué IEnumerable<T> Heredar del IEnumerable pero IList<T> no hereda de IList?

Una secuencia de enteros se puede tratar como una secuencia de objetos, encajonando cada entero como sale de la secuencia. Pero una lista de lectura y escritura de enteros no puede tratarse como una lista de objetos de lectura y escritura, porque puede poner una cadena en una lista de objetos de lectura y escritura. No es necesario un IList<T> para cumplir con el contrato completo de IList, por lo que no hereda de él.

+0

upvoted. La pregunta extra se realiza este año: [[¿Por qué IList genérico <> no hereda IList no genérico] (http://stackoverflow.com/questions/14559070/why-generic-ilist-does-not-inherit-non- generic-ilist)] –

3
  1. Sí lo hacen, porque List<T> puede tener las propiedades y el método para cumplir con todas esas interfaces. No sabe qué interfaz va a haber declarado alguien como parámetro o valor de retorno, por lo que cuanto más listas implemente la interfaz, más versátil puede ser.
  2. Su código se compilará porque el upcasting (var a1 = (I1)a;) falla en el tiempo de ejecución no en tiempo de compilación. Puedo hacer var a1 = (int)a y hacer que compile.
+0

parece estar funcionando ... agregué console.readline hasta el final y obtuvo mi entrada y finalizó sin ningún error – idm

+0

Funcionará bien. ¿Qué te hace pensar que no? –

5

Las interfaces no genéricas son para compatibilidad con versiones anteriores. Si escribe código usando genéricos y desea pasar su lista a algún módulo escrito en .NET 1.0 (que no tiene genéricos), aún desea que esto tenga éxito. Por lo tanto, IList, ICollection, IEnumerable.

2

List<> implementa todas esas interfaces para exponer la funcionalidad descrita por las diferentes interfaces. Comprende las características de una Lista genérica, Colección y Enumerable (con compatibilidad hacia atrás con los equivalentes no genéricos)

5

Gran pregunta y buena respuesta de Eric Lippert. Esta pregunta vino a mi mente en el pasado, y entiendo lo siguiente, espero que te ayude.

Supongamos que soy un programador de C# en otro planeta del universo, en este planeta, todos los animales pueden volar.Así que mi programa se parece a:

interface IFlyable 
{ 
    void Fly(); 
} 
interface IAnimal : IFlyable 
{ 
    //something 
} 
interface IBird : IAnimal, IFlyable 
{ 
} 

Bien puede ser confundido, ya que son BirdsAnimals, y todos Animals puede volar, ¿por qué necesitamos para especificar IFlyable en la interfaz IBird? OK Vamos a cambiarlo a:

interface IBird : IAnimal 
{ 
} 

estoy 100% seguro de que el programa funciona como antes, por lo que nada se cambia? El IFlyable en la interfaz IBird es totalmente inútil? Continúemos.

Un día, mi compañía vendió el software a otra compañía en la Tierra. Pero en la Tierra, ¡no todos los animales pueden volar! Por supuesto, necesitamos modificar la interfaz IAnimal y las clases que la implementan. Después de la modificación, encontré que IBird es incorrecto ahora porque los pájaros en la tierra pueden volar. Ahora lamento eliminar IFlyable de IBird!

+0

Err ... +1 para el Fly –

+0

Excepto que la respuesta de Eric dice que el _source code_ en realidad solo tiene el equivalente de IBird: IAnimal, y que el IFlyable solo aparece en MSDN, ILSpy, etc., como adicional (información deducida) para ayudarlo. –

+0

Pero luego tiene que pasar por todas las otras interfaces 'IDog',' ICat', 'IRhinoceros', etc, que presumiblemente también agregó' IFlyable' en su universo de moscas de todos los animales, y * eliminar * IFlyable de ellos. De cualquier manera, tienes muchas actualizaciones por hacer. – Blorgbeard

Cuestiones relacionadas