2

I was going through object initialization and constructor initialization for my object, but couldn't get exact reply to my question. What is the difference between Case1 and Case2 here;

Case 1:

namespace ConsoleApplication2
{
    class MyBuilder
    {
        private MySynchronizer m_synchronizer = new MySynchronizer();

        public MyBuilder()
        {

        }

        public void ProcessRecord(int recordNumber)
        {
            m_synchronizer.Process(recordNumber);
        }
    }
}

Case II:

namespace ConsoleApplication2
{
    class MyBuilder
    {
        private MySynchronizer m_synchronizer;

        public MyBuilder()
        {
          m_synchronizer = new MySynchronizer();

        }

        public void ProcessRecord(int recordNumber)
        {
            m_synchronizer.Process(recordNumber);
        }
    }
}

This is sample code to show how I am calling my Builder class;

 class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Press any key to stop");
            MyBuilder builder = new MyBuilder();
            builder.ProcessRecord(2);
        }
    }

[Sorry, if I could not have rephrase the question properly, in which case anyone can provide the link to other SO article]

OmGanesh
  • 952
  • 1
  • 12
  • 24
  • "virtually nothing"... except perhaps the order with base constructors. Are you seeing a difference somewhere? – Marc Gravell Jan 30 '19 at 17:44
  • 1
    If you initialize a member variable when it is declared (rather than doing it in a constructor), that initialization code is prepending to the body of any constructor you might have. Put a breakpoint on the initialization code, and on the first statement of your constructor (add a second constructor if you want to see how that works) and debug your code. Mostly, it's a matter of taste, but it's particularly handy if your class has multiple constructors – Flydog57 Jan 30 '19 at 17:46
  • The biggest different is when it get done. When you put something into a constructor you know it will "always" get done. Putting something in a method means you have to call the method and in the future somebody changes the code it might not get done or done after it is needed. – jdweng Jan 30 '19 at 18:22
  • @Flydog57 Agreed. It comes handy if you have multiple constructors and less error prone as in case2 someone could easily add another constructor and forget to chain it. – OmGanesh Jan 30 '19 at 19:00

3 Answers3

8

The difference here is really subtle, and can only easily be appreciated in IL:

class MyBuilder1
{
    private MySynchronizer m_synchronizer = new MySynchronizer();

    public MyBuilder1()
    {

    }
}

gives us the constructor:

.method public hidebysig specialname rtspecialname 
    instance void .ctor () cil managed 
{
    // Method begins at RVA 0x2050
    // Code size 18 (0x12)
    .maxstack 8

    IL_0000: ldarg.0
    IL_0001: newobj instance void MySynchronizer::.ctor()
    IL_0006: stfld class MySynchronizer MyBuilder1::m_synchronizer
    IL_000b: ldarg.0
    IL_000c: call instance void [mscorlib]System.Object::.ctor()
    IL_0011: ret
} // end of method MyBuilder1::.ctor

where-as this:

class MyBuilder2
{
    private MySynchronizer m_synchronizer;

    public MyBuilder2()
    {
      m_synchronizer = new MySynchronizer();

    }
}

gives us:

// Methods
.method public hidebysig specialname rtspecialname 
    instance void .ctor () cil managed 
{
    // Method begins at RVA 0x2063
    // Code size 18 (0x12)
    .maxstack 8

    IL_0000: ldarg.0
    IL_0001: call instance void [mscorlib]System.Object::.ctor()
    IL_0006: ldarg.0
    IL_0007: newobj instance void MySynchronizer::.ctor()
    IL_000c: stfld class MySynchronizer MyBuilder2::m_synchronizer
    IL_0011: ret
} // end of method MyBuilder2::.ctor

The difference is simply one of ordering:

  • field initializers (MyBuilder1) happen before the base-type constructor call (object is the base here; call instance void [mscorlib]System.Object::.ctor() is the base-constructor call)
  • constructors happen after the base-type constructor call

In most cases, this won't matter. Unless your base-constructor invokes a virtual method that the derived type overrides: then whether or not the field has a value in the overridden method will be different between the two.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • How might someone be able to see the IL as you've posted? – Cory Jan 30 '19 at 17:49
  • 2
    @Cory I use sharplab as my test bench for small pieces; for larger pieces: reflector - but: [try this link](https://sharplab.io/#v2:EYLgtghgzgLgpgJwDQxASwDYB8AEe8ACATDgLICeAQgK6YAmiAjALABQ+OA3mxxwA4I0ANwjwy5AMrkAdgGMAFggD20tAC9EOMAH0oMhctUaEOALw5pcAO7ipcxSvWIAFAEoA3Gx69CAZnE09Exu3j7crKG8AL6RMez4xAG0GAwIRJHhPngCwqJwtvoORpo6evaGTgieEfE+BP4UgSmIRCG1vJlZeKWFFcZmFtYF5Y7GbtWRHHFTbIkUdgajmpxRQA==) – Marc Gravell Jan 30 '19 at 17:50
  • @Cory actually, one of the most useful features for me is to have the output as C# - which might sound odd, but you can use that to see how the compiler interprets complex concepts like iterators, lambdas, etc. – Marc Gravell Jan 30 '19 at 17:54
  • yeah, I've seen ton of posts showing how things like async/await are treated by the compiler... this tool will be great to show that. – Cory Jan 30 '19 at 18:06
  • Yes @MarcGravell The difference is observed in the order of the constructor as explained by you in IL code. Thanks man for in-depth explanation. – OmGanesh Jan 30 '19 at 19:13
3

As @Marc already mentioned, the difference is in the order of the base constructor.

I have added the base constructor

 class Base
    {
        public Base()
        {
            Console.WriteLine("Inside Base constructor");
        }
    }

and modified my class "MyBuilder" to derived from it as;

 class MyBuilder : Base
    {

    }

Now, the output from case1 looks like:

enter image description here

whereas from case2:

enter image description here

Hence,

  • If you have multiple constructor, then the case1 approach might be better, since there is less error prone as someone could easily add another constructor and forget to chain it.
  • If you have single constructor and no any logic flow that depends on base constructor order, then the case2 seems better, as that will make the code cleaner. [no offence, personal preference]
OmGanesh
  • 952
  • 1
  • 12
  • 24
0

I almost always choose the second one option (initializing inside the constructor). In my point of view it keeps your code more readable and the control logic is inside the constructor which gives more flexibility to add logic in the future.

But again, it is only my personal opinion.

leandro.andrioli
  • 997
  • 5
  • 12