2011-04-27 12 views
5

Soy principalmente un desarrollador de C++, pero recientemente he estado trabajando en un proyecto en C#. Hoy encontré un comportamiento inesperado, al menos para mí, al usar los inicializadores de objetos. Espero que alguien aquí pueda explicar lo que está pasando.En C#, ¿cómo interactúan los inicializadores de objetos y los inicializadores de objetos?

Ejemplo A

public class Foo { 
    public bool Bar = false; 
} 

PassInFoo(new Foo { Bar = true }); 

Ejemplo B

public class Foo { 
    public bool Bar = true; 
} 

PassInFoo(new Foo { Bar = false }); 

Ejemplo A funciona como yo esperaría. El objeto pasado a PassInFoo tiene Bar establecido en verdadero. Sin embargo, en el Ejemplo B, foo.Bar se establece en verdadero, a pesar de que se le asignó false en el inicializador de objetos. ¿Qué estaría causando que el inicializador de objetos en el Ejemplo B fuera aparentemente ignorado?

+6

Nada. No estás viendo lo que crees que estás viendo. – mquander

+3

No puedo reproducir esto, .Net 3.5, ¿qué versión tiene y está seguro de que este es el comportamiento que está viendo? –

+2

El constructor se ejecutará antes de que el inicializador de objetos y el inicializador de campo sean parte del constructor. Ver C# Specs: de 7.6.10 - El procesamiento de una expresión de creación de objeto que incluye un inicializador de objeto o inicializador de colección consiste en procesar primero el constructor de instancia y luego procesar las inicializaciones de miembro o elemento especificadas por el inicializador de objeto (§7.6.10.2) o inicializador de colección (§7.6. 10.3). – VinayC

Respuesta

3

La manera más fácil de ver lo que está sucediendo es dividir su extracto en su equivalente si se hace línea por línea.

original:

PassInFoo(new Foo { Bar = false }); 

estallado:

var tmp = new Foo(); //Bar initialized to true 
tmp.Bar = false; 
PassInFoo(tmp); 
+0

Por cierto, probé lo anterior en .NET 3.5 y 4.0 y en ambos casos, Bar es de hecho falso dentro de 'PassInFoo'. – Thomas

+0

Acabo de probar esto en mi proyecto Unity3D y funcionó correctamente. Unity o MonoDevelop deben estar haciendo una "optimización" de ignorar las asignaciones del inicializador de objetos si las asignaciones son del valor predeterminado del tipo (falso para bool en mi código de ejemplo). Probablemente tendré que llevar esta pregunta a esas comunidades para obtener una respuesta más específica. – nschrag

+2

@nschrag - Sería muy perturbador descubrir que uno de esos entornos estaba jugando con el orden de evaluación. ¡Buena suerte! – Thomas

2
class Program 
{ 
    static void Main(string[] args) 
    { 
     PassInFoo(new Foo { Bar = false }); 
    } 
    public class Foo 
    { 
     public bool Bar = true; 
    } 

    public static void PassInFoo(Foo obj) 

    { 
     Console.WriteLine(obj.Bar.ToString()); 
     Console.ReadLine(); 
    } 
} 

El código anterior funciona bien cuando son verificadas con Framework 3.5 (falso se mostrará en la ventana de la consola).

3

Tuve un problema similar con Unity & Mono.

Lo solucioné inicializando los campos en el constructor y luego sobrescribiendo con los inicializadores de objetos.

¡SIN EMBARGO! Al principio esto tampoco funcionó. Así que reemplacé los campos con propiedades con campos de respaldo y parecía forzar el comportamiento esperado.

5

Confirmo este feo error en la creación de Unity3d de Mono (Mono 2.6.5, Unity3d 4.1.2f1, OSX).

Parece que no le gusta usar el valor por defecto para el ValueType, por lo que puede pasar un int != 0, etc (bool)true muy bien, pero que pasa en el valor por defecto como (int)0 o (bool)false ignora su valor.

Prueba:

using UnityEngine; 
using System.Collections; 

public class Foo1 { 
    public bool Bar=false; 
} 

public class Foo2 { 
    public bool Bar=true; 
} 

public class Foo1i { 
    public int Bar=0; 
} 

public class Foo2i { 
    public int Bar=42; 
} 

public class PropTest:MonoBehaviour { 

    void Start() { 
     PassInFoo(new Foo1 {Bar=true}); // FOO1: True (OK) 
     PassInFoo(new Foo2 {Bar=false});/// FOO2: True (FAIL!) 
     PassInFoo(new Foo1i {Bar=42}); // FOO1i: 42 (OK) 
     PassInFoo(new Foo2i {Bar=0});/// FOO2i: 42 (FAIL!) 
     PassInFoo(new Foo2i {Bar=13});/// FOO2i: 13 (OK) 
    } 

    void PassInFoo(Foo1 f) {Debug.Log("FOO1: "+f.Bar);} 

    void PassInFoo(Foo2 f) {Debug.Log("FOO2: "+f.Bar);} 

    void PassInFoo(Foo1i f) {Debug.Log("FOO1i: "+f.Bar);} 

    void PassInFoo(Foo2i f) {Debug.Log("FOO2i: "+f.Bar);} 
} 

En un no-Unity3D OSX Mono 2.10.11 (mono-2-10/2baeee2 Mir 16 Ene 16:40:16 EST 2013) las pruebas están funcionando muy bien:

FOO1: True 
FOO2: False 
FOO1i: 42 
FOO2i: 0 
FOO2i: 13 

EDIT: rellenado un error en bugtracker de Unity3D: https://fogbugz.unity3d.com/default.asp?548851_3gh8hi55oum1btda

+1

Acaba de comprobarse en Unity 5 (solo editor): esta falla persiste. Feliz depuración. –

+1

Acabo de recibir un mensaje de Unity QA: corregirá este error en una versión futura no especificada. –

+0

cosas impresionantes, @ChanibaL – Fattie

Cuestiones relacionadas