In your case, there's no real difference in the functionality. There is the problem of figuring out where and how everything gets initialized, though. If i put the initialization in the constructor, i have two big benefits:
Everything's initialized in one place; i don't have to go hunting for whether and where it gets set.
If i want, i can pass arguments to the Bar constructor based on how i'm setting stuff up. If the initializer's outside the constructor, i'm a lot more limited in how i can initialize stuff.
Frankly, the IDE helps a bit with #1...though it does yank me away from the code i was just looking at, my classes are rarely so huge as to make re-finding stuff an issue. So if i don't need #2, i'm just as likely to do it either way depending on the project and my mood. For bigger projects, though, i'd want to have all my init code in one place.
Edit:
OK, apparently there is a difference between the two. But the case where it matters is rare.
I've created the following classes:
class Program
{
public Program() { Console.WriteLine(this); }
static void Main(string[] args)
{
other p = new other();
Console.WriteLine(p);
Console.ReadLine();
}
}
class other : Program
{
string s1 = "Hello";
string s2;
public other() { s2 = "World"; }
public override string ToString() { return s1 + s2; }
}
Now, what i found was a bit surprising, and unexpected (if you haven't read the C# spec).
This is what the other
class's constructor compiled to:
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 29 (0x1d)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldstr "Hello"
IL_0006: stfld string ConsoleApplication1.other::s1
IL_000b: ldarg.0
IL_000c: call instance void ConsoleApplication1.Program::.ctor()
IL_0011: ldarg.0
IL_0012: ldstr "World"
IL_0017: stfld string ConsoleApplication1.other::s2
IL_001c: ret
} // end of method other::.ctor
Note the call to Program::.ctor (the base class's constructor) sandwiched between the two ldstr
/stfld
pairs (those are what set s1
and s2
). Meaning, when the base constructor is running, s2
has not been set yet.
The program, for reference, outputs the following:
Hello
HelloWorld
because in Program's constructor, Console.WriteLine(obj)
called obj.ToString()
, which (since the object is already an other
) was other::ToString()
. Since s2
wasn't set yet, we didn't get a "World" the first time. If we were doing something more error prone than just printing stuff, this could cause real problems.
Now, this is an ugly example, designed to be a pathological case. But it's an excellent argument against calling virtual functions in your constructor. Without doing just that, this breakage would not have been possible. That's the only time you really have to worry about the difference: when your base class's constructor is calling virtual methods that you've overridden, that rely on the values of any fields that are being set in the constructor. A pretty narrow window of breakability, but yeah.