I have a class with a field that needs to be initialized when the object is initialized, such as a list that needs to be created before objects can be added/removed from it.
public class MyClass1
{
private List<MyOtherClass> _otherClassList;
public MyClass1()
{
this._otherClasslist = new List<MyOtherClass>();
}
}
public class MyClass2
{
private List<MyOtherClass> = new List<MyOtherClass>();
public MyClass2()
{
}
}
What is the difference between these two classes, and why would you choose one method over the other?
I usually set the field in the constructor, as in MyClass1, because I find it easier to be able to look in one place to see all the stuff that happens when the object is being instantiated, but is there any case where it is better to initialize a field directly like in MyClass2?
The ILs emitted by C# compiler (VS2008 sp1) will be almost equivalent for both cases (even in Debug and Release builds).
However, if you need to add parameterized constructors that take List<MyOtherClass>
as an argument, it will be different (especially, when you will create a significantly large number of objects with such constructors).
See the following examples to see the differences (you can copy&past to VS and build it to see ILs with Reflector or ILDASM).
using System;
using System.Collections.Generic;
namespace Ctors
{
//Tested with VS2008 SP1
class A
{
//This will be executed before entering any constructor bodies...
private List<string> myList = new List<string>();
public A() { }
//This will create an unused temp List<string> object
//in both Debug and Release build
public A(List<string> list)
{
myList = list;
}
}
class B
{
private List<string> myList;
//ILs emitted by C# compiler are identicial to
//those of public A() in both Debug and Release build
public B()
{
myList = new List<string>();
}
//No garbage here
public B(List<string> list)
{
myList = list;
}
}
class C
{
private List<string> myList = null;
//In Release build, this is identical to B(),
//In Debug build, ILs to initialize myList to null is inserted.
//So more ILs than B() in Debug build.
public C()
{
myList = new List<string>();
}
//This is identical to B(List<string> list)
//in both Debug and Release build.
public C(List<string> list)
{
myList = list;
}
}
class D
{
//This will be executed before entering a try/catch block
//in the default constructor
private E myE = new E();
public D()
{
try
{ }
catch (NotImplementedException e)
{
//Cannot catch NotImplementedException thrown by E().
Console.WriteLine("Can I catch here???");
}
}
}
public class E
{
public E()
{
throw new NotImplementedException();
}
}
class Program
{
static void Main(string[] args)
{
//This will result in an unhandled exception.
//You may want to use try/catch block when constructing D objects.
D myD = new D();
}
}
}
Note: I did not change any optimization flag when switching to Release build.
There is one difference:
Fields are initialized immediately
before the constructor for the object
instance is called, so if the
constructor assigns the value of a
field, it will overwrite any value
given during field declaration. From
MSDN:
Behavior-wise both should be identical.
However something you may want to consider is an edge-case IL - Bloat. The IL for the field initializers is inserted to the top of each ctor. And from that it follows that if you have many field initializers and many overloaded ctors, the same section of IL is prefixed to the ctor overload IL. As a result the total size of your assembly might go up as compared to the case where you use constructor chaining or delegate to a common Initialize()
function (where the repeated IL would be a method call). hence for this specific scenario, field initializers would be a relatively weaker choice.
You can verify this with reflector on the binary for this code snippet
public class MyClass2
{
private List<int> whack = new List<int>();
// lots of other field initializers
public MyClass2()
{
Console.WriteLine("Default ctor");
}
public MyClass2(string s)
{
Console.WriteLine("String overload");
}
// lots of other overload ctors
}
When initializing in a constructor I would think it would be easier to catch and handle exceptions if such is necessary.
In MyClass1 someone could override the constructor and cause issues.
The codes are equivalent because compiler put initialization in every constructor you have in second case, the benefit of second case is that the programmer will not think to initialize fields when he will add new constructor after year of writing that class :)
In c# there is practically no difference between your two examples. But, developer tends to initialize their fields in the constructor because it less error prone.