2010-02-23 13 views
6

Solo me interesaba el código siguiente.Creación de objetos en Loop C#

for (int i=0; i<9; i++) 
{ 
    ClassA objectA = new ClassA(); 
} 

o

ClassA objectA; 
for (int i=0; i<9; i++) 
{ 
    objectA = new ClassA(); 
} 

Cualquier idea es que hay alguna diferencia, tanto para el código? Según mi conocimiento, ambos crearán instancias diferentes cada vez, por lo que el número de instancias será el mismo. ¿Alguna idea?

Respuesta

25

de alcance a un lado (es decir, si existe la variable fuera del bucle) hay generalmente ninguna diferencia, ya que las variables .NET son en realidad (en el IL), todo al inicio del método de todos modos. Sin embargo, hay una excepción: si captura la variable (en un método anónimo/lambda), entonces se comporta de manera diferente - la captura se construye para respetar la declaración C#. Por lo tanto:

List<Action> actions = new List<Action>(); 
ClassA objectA; 
for (int i=0;i<9;i++) 
{ 
    objectA= new ClassA(); 
    actions.Add(delegate { Console.WriteLine(objectA.GetHashCode()); });   
} 
foreach(Action action in actions) action(); 

y:

List<Action> actions = new List<Action>(); 
for (int i=0;i<9;i++) 
{ 
    ClassA objectA= new ClassA(); 
    actions.Add(delegate { Console.WriteLine(objectA.GetHashCode()); });   
} 
foreach(Action action in actions) action(); 

hará cosas diferentes (las primeras impresiones de los mismos basada en direcciones de código hash de 9 veces; la segunda imprime 9 diferente de hash basado en direcciones -códigos, lo que indica que en el segundo ciclo hemos capturado 9 variables diferentes, en lugar de una sola variable).

En ambos casos hay 9 ClassA objetos creados, simplemente que no podemos ver 8 más en el primer caso.

+4

+1 porque eso es realmente interesante. –

+1

Como siempre, Marc, entregas más allá de lo esperado. El alcance cuando se hace lambda es una tuerca difícil de romper a veces.Estoy seguro de que todos nos hemos quemado por el problema de alcance que señalas arriba. Sé que tengo. ¡Aclamaciones! – TheSoftwareJedi

0

La gran diferencia es si crea un cierre dentro del bucle que captura el objeto A, y ese cierre se escapa del bucle. En otras palabras, si objectA es de alguna manera visible fuera del ciclo, entonces la primera instancia tendrá 9 valores diferentes después de que termine el ciclo, mientras que el segundo solo tendrá el último valor una vez que finalice el ciclo.

Por ejemplo,

for (int i = 0; i < 9; i++) 
{ 
    ClassA objectA = new ClassA(); 
    someFunc(() => objectA); 
} 

podría tener resultados visibles diferentes que

ClassA objectA; 
for (int i = 0; i < 9; i++) 
{ 
    objectA = new ClassA(); 
    someFunc(() => objectA); 
} 
+0

¿Por qué destacas específicamente los cierres aquí? 'someFunc (objectA)' también podría estar guardando una referencia a cada objeto, por ejemplo, si 'someFunc' es una función' Add' de la Colección. –

+0

'someFunc (objectA)' pasará una referencia a un objeto diferente cada iteración a través del ciclo; 'someFunc (() => objectA)' pasará la misma o diferente referencia dependiendo de dónde se declare 'ClassA'. – Gabe

13

La única diferencia es que en el segundo ejemplo, la última instancia creada todavía será accesible después de el bucle

0

La diferencia entre los dos es que una vez completado el ciclo for, la instancia de ClassA de th La última iteración (i = 8) estará disponible para el resto de la función, mientras que en la primera versión, no lo será.

0

La única diferencia es que el objetoA apuntará a un ejemplo de Clase A después del segundo ciclo. En ambos casos se crearán 9 objetos, pero en el primer caso se destruirán todos, y en el segundo, el último objeto no se destruirá.

2

En realidad, como nunca guarda una referencia a los objetos que crea, cada vez que sobrescribe objectA, habrá eliminado el ejemplo anterior.

Sin embargo, en el caso de:

for (int i=0;i<9;i++) 
{ 
    ClassA objectA = new ClassA(); 
} 

objectA no existe fuera del alcance del bucle, y todos los objetos que hemos creado finalmente será eliminado por el recolector de basura. Sería un error de sintaxis hacer referencia a objectA fuera del ciclo.

Por el contrario, en el caso de:

ClassA objectA; 
for (int i=0;i<9;i++) 
{ 
    objectA= new ClassA(); 
} 

objectA es un identificador válido fuera del bucle, y la instancia final del ClassA creado permanecerá después de que se completó el bucle.

1

En el primer fragmento, objectA no será accesible fuera del ciclo. Por lo tanto, si escribió:

for (int i=0;i<9;i++) 
{ 
    ClassA objectA = new ClassA(); 
} 
// This will not compile. 
objectA.DoSomething(); 

Recibirá un error de compilación. Sin embargo, si usted escribió el código como:

ClassA objectA; 
for (int i=0;i<9;i++) 
{ 
    objectA= new ClassA(); 
} 
// This will compile. 
objectA.DoSomething(); 

El segundo fragmento de código se compilará (suponiendo claseA tiene un método denominado HacerAlgo()).

0

prakash, solo por el bien de la corrección/integridad: no es cierto que no se podrá acceder a las variables una vez que finalice el ciclo. Normalmente asumimos que el objeto que se está creando es muy simple, que podría no ser el caso. Por ejemplo, puede agregar el objeto a una lista global dentro del constructor, de modo que todos los objetos sigan siendo accesibles después de que termine el ciclo.

La diferencia relevante es la señalada por kbrinley, porque no se puede usar la variable (que es una referencia de objeto) fuera del alcance (lo que sea entre {y}) en el primer ejemplo. Como en el segundo declaras la variable fuera del ciclo, aún puedes usar la variable.

Como dijo Marc Gravell, la IL generado es el mismo para ambos, por lo que no debería haber ninguna diferencia en términos de rendimiento, la ocupación de memoria, etc., para el bucle de. [1]


1: Dado que en el segundo ejemplo ciertamente se conserva una referencia a la última variable, el recolector de basura no podrá liberar su espacio. Entonces, después de que termine el ciclo, habrá diferencias sutiles.

2

El número de instancia no se incrementa según los códigos. Ambos bucles recién están reinicializando una misma variable, la única diferencia es que en el primer bucle no se puede acceder a la variable de instancia cuando finaliza el bucle, a diferencia del segundo bucle en el que la variable se declara fuera del bucle.