2009-01-19 7 views
55

estaba mirando C inicializadores # recolección y conocer la aplicación a ser muy pragmáticos, pero también muy diferente a cualquier otro en C#¿Por qué los inicializadores de colección C# funcionan de esta manera?

soy capaz de crear un código como éste:

using System; 
using System.Collections; 

class Program 
{ 
    static void Main() 
    { 
     Test test = new Test { 1, 2, 3 }; 
    } 
} 

class Test : IEnumerable 
{ 
    public IEnumerator GetEnumerator() 
    { 
     throw new NotImplementedException(); 
    } 

    public void Add(int i) { } 
} 

Desde que satisfagan los requisitos mínimos para el compilador (implementado IEnumerable y public void Add) esto funciona, pero obviamente no tiene ningún valor.

Me preguntaba ¿qué impedía que el equipo de C# creara un conjunto más estricto de requisitos? En otras palabras, ¿por qué, para que esta sintaxis se compile, el compilador no requiere que el tipo implemente ICollection? Eso parece más en el espíritu de otras características de C#.

+3

Solo una nota para los desarrolladores web: se lanzará NotImplementedException si intentó serializar una instancia de esa clase a JSON (la implementación de IEnumerable hace que el serializador itere a través del objeto) – diachedelic

Respuesta

87

Su observación es perfecta, de hecho, refleja una hecha por Mads Torgersen, un Microsoft C# Language PM.

Mads hizo un post en octubre de 2006 sobre este tema titulado What Is a Collection? en el que escribió:

admitió que lo arruinamos en la primera versión del marco con System.Collections.ICollection, que es casi inútil. Pero lo arreglamos hasta bastante bien cuando llegaron a lo largo de los genéricos en marco .NET 2.0: System.Collections.Generic.ICollection < T> le permite añadir y eliminar elementos, ellos enumerar, contarlos y comprobar para la adhesión.

Obviamente partir de entonces, todo el mundo aplicar ICollection <T> cada vez que hacen una colección, ¿verdad? No tan. Así es como usamos LINQ para aprender sobre qué colecciones realmente son, y cómo eso nos hizo cambiar nuestro lenguaje diseño en C# 3.0.

Resulta que sólo hay 14 implementaciones de ICollection<T> en el marco, pero 189 clases que implementan IEnumerable y tienen un método público Add().

Este enfoque tiene un beneficio oculto: si lo hubieran basado en la interfaz ICollection<T>, habría existido exactamente un método compatible con Add().

En contraste, el enfoque que tomaron significa que los inicializadores para la colección solo forman conjuntos de argumentos para los métodos Add().

Para ilustrar, vamos a ampliar su código levemente:

class Test : IEnumerable 
{ 
    public IEnumerator GetEnumerator() 
    { 
     throw new NotImplementedException(); 
    } 

    public void Add(int i) { } 

    public void Add(int i, string s) { } 
} 

ahora se puede escribir lo siguiente:

class Program 
{ 
    static void Main() 
    { 
     Test test 
      = new Test 
      { 
       1, 
       { 2, "two" }, 
       3 
      }; 
    } 
} 
+0

En la cita, 2 ° párrafo, primera línea, es "ICollection " no "ICollection". –

+0

Tiene razón, descuidé la codificación de los caracteres menores que y mayores. Fijo. – Bevan

+0

Su respuesta ejemplifica los beneficios/flexibilidad que esto proporciona, pero me pregunto si esto significa que no existe una manera efectiva de brindar dicha flexibilidad en escenarios similares. De hecho, escribieron en la especificación "si tiene un método llamado 'Add' en alguna parte de una jerarquía que implementa 'IEnumerable', entonces haremos algunos hokus-pokus en el compilador y los inicializadores lo llamarán, aunque el método sea no es una sobrecarga, miembro de la interfaz, o de ninguna manera designada para tal fin a través de sintaxis, atributos u otros medios ". – AaronLS

9

pensé en esto también, y la respuesta que me satisface más es que ICollection tiene muchos métodos además de Agregar, como: Borrar, Contiene, CopiarA y Eliminar.La eliminación de elementos o el borrado no tiene nada que ver con la capacidad de soportar la sintaxis del inicializador de objetos, todo lo que necesita es un Add().

Si el marco se diseñó con gran detalle, y existía una interfaz ICollectionAdd, entonces habría tenido un diseño "perfecto". Pero, sinceramente, no creo que hubiera agregado mucho valor, teniendo un método por interfaz. IEnumerable + Add parece un enfoque hackish, pero cuando lo piensas, es una mejor alternativa.

EDITAR: Esta no es la única vez que C# ha enfrentado un problema con este tipo de solución. Desde .NET 1.1, foreach usa tipado de pato para enumerar una colección, toda su clase necesita implementar es GetEnumerator, MoveNext y Current. Kirill Osenkov tiene un post que también hace su pregunta.

+0

Ese es el "principio de segregación de interfaz". –

3

(sé que soy 3 años de retraso en esto, pero yo no estaba satisfecho con las respuestas existentes.)

por qué, a fin de que esta sintaxis para compilar, hace que el compilador no requieren que el tipo implementar ICollection?

Voy a invertir su pregunta: ¿De qué serviría si el compilador tuviera requisitos que realmente no son necesarios?

Las clases que no son ICollection también pueden beneficiarse de la sintaxis del inicializador de recopilación. Considere las clases que permiten agregar datos en ellos, sin permitir el acceso a datos previamente agregados.

Personalmente, me gusta usar la sintaxis new Data { { ..., ... }, ... } para agregar una apariencia ligera, tipo DSL, al código de mis pruebas unitarias.

En realidad, prefiero debilitar el requisito, de modo que pueda usar la sintaxis de aspecto agradable sin siquiera tener que preocuparme de implementar IEnumerable. Los inicializadores de colecciones son azúcar sintáctico puro para Agregar(), no deberían requerir nada más.

+4

Veo tu punto, pero si el azúcar sintáctico requiere un método, prefiero aprender sobre eso a través de una interfaz. –

+0

@ nik.shornikov Algo así como ICollectionInitializerSyntax (y todas las variantes )? Estoy de acuerdo, esto haría que la intención del código sea más clara. Y funcionaría muy bien con mi deseo de debilitar el requisito actual de IEnumerable. –

Cuestiones relacionadas