c# compiler-errors

c# - Lista<int> test={1, 2, 3}-¿es una característica o un error?



c# documentation (6)

Esperaría que ese código dara un error en tiempo de compilación.

Como su expectativa es contraria tanto a la especificación como a la implementación, su expectativa no se cumplirá.

¿Por qué no falla en el momento de la compilación?

Porque la especificación establece específicamente que es legal en la sección 7.6.10.2, que cito aquí para su conveniencia:

Un inicializador de miembro que especifica un inicializador de recopilación después del signo igual es una inicialización de una colección incrustada. En lugar de asignar una nueva colección al campo o a la propiedad, los elementos dados en el inicializador se agregan a la colección a la que hace referencia el campo o la propiedad.

¿Cuándo se ejecutaría dicho código correctamente?

Como dice la especificación, los elementos dados en el inicializador se agregan a la colección a la que hace referencia la propiedad. La propiedad no hace referencia a una colección; es nulo Por lo tanto, en tiempo de ejecución da una excepción de referencia nula. Alguien tiene que inicializar la lista. Recomendaría cambiar la clase "Prueba" para que su constructor inicialice la lista.

¿Qué escenario motiva esta característica?

Las consultas LINQ necesitan expresiones, no declaraciones. Agregar un miembro a una colección recién creada en una lista recién creada requiere llamar a "Agregar". Debido a que "Agregar" es inválido, una llamada solo puede aparecer en una declaración de expresión. Esta función le permite crear una nueva colección (con "nuevo") y llenarla, o completar una colección existente (sin "nueva"), donde la colección es miembro de un objeto que está creando como resultado de un LINQ consulta.

Como sabe, no está permitido usar la sintaxis de inicialización de matriz con Listas. Le dará un error en tiempo de compilación. Ejemplo:

List<int> test = { 1, 2, 3} // At compilation the following error is shown: // Can only use array initializer expressions to assign to array types.

Sin embargo, hoy hice lo siguiente (muy simplificado):

class Test { public List<int> Field; } List<Test> list = new List<Test> { new Test { Field = { 1, 2, 3 } } };

El código anterior compila muy bien, pero cuando se ejecuta dará un error en tiempo de ejecución "Las referencias de objeto no están configuradas en un objeto".

Esperaría que ese código dara un error en tiempo de compilación. Mi pregunta para usted es: ¿por qué no lo hace, y hay alguna buena razón para cuando ese escenario se ejecutaría correctamente?

Esto ha sido probado usando .NET 3.5, ambos compiladores .Net y Mono.

Aclamaciones.


Cambia tu código a esto:

class Test { public List<int> Field = new List<int>(); }

La razón es que debe crear explícitamente un objeto de colección antes de poder ponerle artículos.


Creo que esto es un comportamiento por diseño. La Test = { 1, 2, 3 } se compila en el código que llama al método Add de la lista almacenada en el campo Test .

El motivo por el que obtiene NullReferenceException es que Test es null . Si inicializa el campo de Test a una nueva lista, entonces el código funcionará:

class Test { public List<int> Field = new List<int>(); } // Calls ''Add'' method three times to add items to ''Field'' list var t = new Test { Field = { 1, 2, 3 } };

Es bastante lógico: si escribe una new List<int> { ... } entonces crea una nueva instancia de lista. Si no agrega construcción de objetos, usará la instancia existente (o null ). Por lo que puedo ver, la especificación de C # no contiene ninguna regla de traducción explícita que coincida con este escenario, pero da un ejemplo (ver la Sección 7.6.10.3 ):

Una List<Contact> se puede crear e inicializar de la siguiente manera:

var contacts = new List<Contact> { new Contact { Name = "Chris Smith", PhoneNumbers = { "206-555-0101", "425-882-8080" } }, new Contact { Name = "Bob Harris", PhoneNumbers = { "650-555-0199" } } };

que tiene el mismo efecto que

var contacts = new List<Contact>(); Contact __c1 = new Contact(); __c1.Name = "Chris Smith"; __c1.PhoneNumbers.Add("206-555-0101"); __c1.PhoneNumbers.Add("425-882-8080"); contacts.Add(__c1); Contact __c2 = new Contact(); __c2.Name = "Bob Harris"; __c2.PhoneNumbers.Add("650-555-0199"); contacts.Add(__c2);

donde __c1 y __c2 son variables temporales que de otro modo serían invisibles e inaccesibles.


Este código:

Test t = new Test { Field = { 1, 2, 3 } };

Se traduce a esto:

Test t = new Test(); t.Field.Add(1); t.Field.Add(2); t.Field.Add(3);

Como Field es null , obtienes la NullReferenceException .

Esto se llama msdn.microsoft.com/en-us/library/bb384062.aspx , y funcionará en su ejemplo inicial si hace esto:

List<int> test = new List<int> { 1, 2, 3 };

Realmente necesita actualizar algo para poder usar esta sintaxis, es decir, un inicializador de colección solo puede aparecer en el contexto de una expresión de creación de objeto. En la especificación C #, sección 7.6.10.1, esta es la sintaxis para una expresión de creación de objeto:

object-creation-expression: new type ( argument-list? ) object-or-collection-initializer? new type object-or-collection-initializer object-or-collection-initializer: object-initializer collection-initializer

Entonces todo comienza con una new expresión. Dentro de la expresión, puede usar un inicializador de colección sin el new (sección 7.6.10.2):

object-initializer: { member-initializer-list? } { member-initializer-list , } member-initializer-list: member-initializer member-initializer-list , member-initializer member-initializer: identifier = initializer-value initializer-value: expression object-or-collection-initializer // here it recurses

Ahora, lo que realmente te estás perdiendo es algún tipo de lista literal, que sería realmente útil. Propuse un tal literal para enumerables here .


La razón para esto es que el segundo ejemplo es un inicializador de listas de miembros, y la expresión MemberListBinding de System.Linq.Expressions da una idea de esto. Consulte la respuesta a esta otra pregunta para obtener más detalles: ¿Cuáles son algunos ejemplos de MemberBinding LINQ? expresiones?

Este tipo de inicializador requiere que la lista ya esté inicializada, de modo que la secuencia que proporcione pueda agregarse.

Como resultado, sintácticamente, no hay absolutamente nada de malo en el código: la NullReferenceException es un error de tiempo de ejecución causado por la Lista que en realidad no se ha creado. Un constructor predeterminado que sea la new lista, o un new en línea en el cuerpo del código, resolverá el error de tiempo de ejecución.

En cuanto a por qué hay una diferencia entre eso y la primera línea de código: en su ejemplo no está permitido porque este tipo de expresión no puede estar en el lado derecho de una tarea porque en realidad no crea nada, solo es una abreviatura para Add .


var test = (new [] { 1, 2, 3}).ToList();