In C#, how do field initializers and object initializers interact?
I'm primarily a C++ developer, but recently I've been working on a project in C#. T开发者_运维知识库oday I encountered some behavior that was unexpected, at least to me, while using object initializers. I'm hoping someone here can explain what's going on.
Example A
public class Foo {
public bool Bar = false;
}
PassInFoo( new Foo { Bar = true } );
Example B
public class Foo {
public bool Bar = true;
}
PassInFoo( new Foo { Bar = false } );
Example A works as I'd expect. The object passed into PassInFoo has Bar set to true. However, in Example B, foo.Bar is set to true, despite being assigned false in the object initializer. What would be causing the object initializer in Example B to be seemingly ignored?
I confirm this ugly bug in Unity3d build of Mono (Mono 2.6.5, Unity3d 4.1.2f1, OSX).
It looks like it doesn't like to use the default value for the ValueType, so you can pass a int != 0
, (bool)true
etc just fine, but passing in the default value like (int)0
or (bool)false
ignores it's value.
Proof:
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);}
}
On a non-unity3d OSX Mono 2.10.11 (mono-2-10/2baeee2 Wed Jan 16 16:40:16 EST 2013) the tests are running fine:
FOO1: True
FOO2: False
FOO1i: 42
FOO2i: 0
FOO2i: 13
EDIT: filled in a bug in unity3d's bugtracker: https://fogbugz.unity3d.com/default.asp?548851_3gh8hi55oum1btda
The easiest way to see what is happening is to break out your statement into its equivalent if done line-by-line.
Original:
PassInFoo( new Foo { Bar = false } );
Broken out:
var tmp = new Foo(); //Bar initialized to true
tmp.Bar = false;
PassInFoo( tmp );
I had a similar problem with Unity & Mono.
I got around it by initializing the fields in the constructor and then overwriting with the object initializers.
HOWEVER ! At first this didn't work either. So I replaced the fields with properties with backing fields and it seemed to force expected behaviour.
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();
}
}
The above code is working fine when verified with Framework 3.5 (false is displayed on the console window).
精彩评论