9

Is there any benefit to doing the following:

public class Foo
{
    private Bar bar;

    public Foo()
    {
        bar = new Bar();
    }
}

Instead of doing it like so:

public class Foo
{
    private Bar bar = new Bar();

    public Foo()
    {
    }
}

Given that at instantiation, the private member variable in either example will be instantiated, I don't believe there is a difference, but I've seen it enough times to where I'm curious.

dreadwail
  • 15,098
  • 21
  • 65
  • 96
  • 1
    Possible duplicate of: http://stackoverflow.com/questions/4219759/create-an-object-in-the-constructor-or-at-top-of-the-class or: http://stackoverflow.com/questions/298183/c-member-variable-initialization-best-practice – Yodan Tauber Jan 23 '11 at 07:56
  • Thanks for catching the dupes, Yodan – dreadwail Jan 23 '11 at 08:02

3 Answers3

19

In the exact case you've given, there isn't a difference - but in general there is.

Variable initializers are executed before the base class constructor is called. If that base constructor calls a virtual method which uses some of the instance variables, you can see that difference.

For spec fans, it's in section 10.11.2 of the C# 4 spec:

When an instance constructor has no constructor initializer, or it has a constructor initializer of the form base(...), that constructor implicitly performs the initializations specified by the variable-initializers of the instance fields declared in its class. This corresponds to a sequence of assignments that are executed immediately upon entry to the constructor and before the implicit invocation of the direct base class constructor.

Here's an example demonstrating this:

using System;

public class Base
{
    public Base()
    {
        Dump();
    }

    public virtual void Dump() {}    
}

class Child : Base
{
    private string x = "initialized in declaration";
    private string y;

    public Child()
    {
        y = "initialized in constructor";
    }

    public override void Dump()
    {
        Console.WriteLine("x={0}; y={1}", x, y);
    }
}

class Test
{
    static void Main(string[] args)
    {
        new Child();
    }
}

Result:

x=initialized in declaration; y=

Now having said the above, I would try hard to avoid calling virtual methods from a constructor. You're basically asking the derived class to work in a partially-initialized fashion. However, it's a difference you should be aware of.

As for where to initialize variables... I have to admit I'm not particularly consistent, and I don't find that's actually a problem. If I have any specific bias, it's probably to initialize anything which won't depend on any parameters at the point of declaration, leaving the variables which I can't initialize without extra information to the constructor.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • @Jon: Eh. I get annoyed by people who quote standards as if they're the Only Way Things Can Possibly Be(tm). Course in this case, it kinda makes more sense; i mean, the people who wrote it, also wrote the One True Implementation. I think i've just been hanging around the C/C++ people too long. :) – cHao Jan 23 '11 at 09:42
  • Although i do have to say, it was kinda fun going in and figuring that stuff out on my own. And i'm more likely to remember it that way. :) – cHao Jan 23 '11 at 09:43
  • 2
    @cHao: The danger is that you rely on *current* behaviour rather than *specified* behaviour though. Of course the spec isn't *always* in sync with real behaviour, but I think it's generally a better thing to rely on. – Jon Skeet Jan 23 '11 at 09:53
  • +1, good answer. I think it might have been helpful to mention initialization of `static` fields in-place vs. in static (class) constructors as a good example when these two do *not* necessarily have the same effect. – stakx - no longer contributing Jan 23 '11 at 09:59
  • @stakx: I think static constructors are sufficiently different to normal constructors that it's worth keeping the two ideas completely separate. (For example, the way that static constructors affect the timing of type initialization has no equivalent in normal constructors.) – Jon Skeet Jan 23 '11 at 10:00
5

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:

  1. Everything's initialized in one place; i don't have to go hunting for whether and where it gets set.

  2. 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.

cHao
  • 84,970
  • 20
  • 145
  • 172
  • Regarding #2, of course; it's a contrived example. Thanks for the note on code cleanliness. I would agree it's cleaner, but wasn't sure if the compiler in the contrived example would treat each in a different manner. (Does not sound like it.) – dreadwail Jan 23 '11 at 08:01
1

Difference is in first case you have bar property initialized after constructor is called and in second before constructor is called. You don't use static methods so there is no difference.

A bit out of topic but best way is to initialise bar outside the object like this:

public class Foo
{
    private Bar bar;

    public Foo( Bar bar )
    {
        this.bar = bar;
    }
}

This way you are not coupling your objects.

smentek
  • 2,820
  • 1
  • 28
  • 32