2011-06-29 20 views
7
public class A 
{ 
    private string _a_string; 
    public string AString 
    { 
     get { return _a_string; } 
     set { _a_string = value; } 
    } 
} 

public class B 
{ 
    private string _b_string; 
    private A _a; 

    public A A 
    { 
     get { return _a; } 
     set { _a = value; } 
    } 

    public string BString 
    { 
     get { return _b_string; } 
     set { _b_string = value; } 
    } 
} 

Esto no funciona:¿Por qué esta asignación C# arroja una excepción?

B _b = new B { A = { AString = "aString" }, BString = "bString" }; 

System.NullReferenceException: referencia a objeto no establecida como una instancia de un objeto.

Esto funciona:

B _b = new B { A = new A { AString = "aString" }, BString = "bString" }; 

Tanto compilarse bien en VS2010.

+0

Gracias por la edición, @ryeguy –

Respuesta

4

No hay instancias de A en B a menos que lo instancias explícitamente como en tu segundo ejemplo.

Cambiar a;

public class B 
{ 
    private string _b_string; 
    private A _a = new A(); 

    public A A 
    { 
     get { return _a; } 
     set { _a = value; } 
    } 

    public string BString 
    { 
     get { return _b_string; } 
     set { _b_string = value; } 
    } 
} 

Para usar el primer ejemplo.

En resumen, sin el nueva A(), no hay A que es la causa de la NullReferenceException

+0

Gracias - Yo sé cómo conseguir que funcione ahora, pero realmente pensé A = {AString = "aString"} fue asignación suficiente. –

5

La línea

B _b = new B { A = { AString = "aString" }, BString = "bString" }; 

es equivalente a

B _b = new B(); 
_b.A.AString = "aString"; // null reference here: _b.A == null 
_b.BString = "bString"; 

Creo que de esta forma está claro lo que está sucediendo.

Compárese esto con la forma equivalente de la expresión que funciona:

B _b = new B(); 
_b.A = new A(); 
_b.A.AString = "aString"; // just fine now 
_b.BString = "bString"; 
+0

Probablemente estés en lo cierto, pero pensé que A = {AString = "aString"} se encargaría de crear una instancia de A por sí mismo. Supongo que tendré que mirar el IL para descubrir realmente ... –

+0

@ OtávioDécio: No, porque el compilador simplemente transforma la sintaxis de "taquigrafía" a la larga. Pero a decir verdad, si me hubieras preguntado hace una hora, no habría reconocido la expresión problemática como C# legal. – Jon

+0

aunque arreglé mi código, creo que este comportamiento es lo suficientemente peligroso como para causar cierta confusión. Supongo que tendré que ser más ortodoxo al inicializar objetos a partir de ahora. –

1

En el primer caso, el código se transforma de manera efectiva por el compilador en

B b = new B(); 
A a = b.A; 
a.AString = "aString"; 
b.BString = "bString"; 

Dado que nunca se le asigna b.A a una instancia, obtiene la excepción (específicamente, ya que nunca asignó b.A, a es nulo y por lo tanto a.AString va a arrojar un NullReferenceException). El hecho de que no hay ningún código del formulario new A... es una gran pista de que nunca se crea una instancia de A.

Usted puede resolver este problema mediante la adición A = new A() al constructor de B, o añadiendo un inicializador de campo para B._a.

En el segundo caso, el código es transformado por el compilador en

B b = new B(); 
A a = new A(); 
a.AString = "aString";  
b.A = a; 
b.BString = "bString"; 

Esto está bien, porque ahora tenemos una instancia de A.

+0

Gracias, pero ¿por qué el compilador no está creando una nueva instancia de A cuando escribo A = {AString = "aString"}? –

+1

Porque no usó 'new A' en ninguna parte. No va a crear una instancia a menos que se lo indique. Usted lo dice usando la palabra clave 'new'. – jason

+0

Porque no hay una instanciación explícita de A dentro de su clase o código. En el punto en que está escribiendo A = {AString = "aString"}, no existe. – ChrisBint

0

No puede utilizar

A = { AString = "aString" } 

debido a la misma razón que hizo

B _b = new B { 

debe declarar una instancia de B (y A) con el fin de usarlo.

Si hubiera escrito

B = B{... 

que se obtendría un error similar

+0

@Reed - Si no puedo está bien, pero si es así, ¿por qué lo está tomando el compilador? –

+0

Porque en tiempo de compilación todavía no está intentando acceder a A, por lo que compilará. El compilador sabe que A es una clase válida, pero no puede determinar en ese momento si se ha instanciado o no, ya que esto podría suceder en otros lugares. – ChrisBint

+0

@Chris - Exactamente. – Reed

Cuestiones relacionadas