.NET object creation, whats faster?

后端 未结 4 597
醉酒成梦
醉酒成梦 2020-12-04 00:26

IS there a difference between those 2 ways of object creation?

new MyClass() { Id = 1, Code = \"Test\" };

or

MyClass c = ne         


        
相关标签:
4条回答
  • 2020-12-04 00:29

    To illustrate M Skeet's code, here's the IL (note the additional ldloc stloc for method #1)

      IL_0001:  newobj     instance void ConsoleApplication1.Program/MyClass::.ctor()
      IL_0006:  stloc.2
      IL_0007:  ldloc.2
      IL_0008:  ldc.i4.1
      IL_0009:  callvirt   instance void ConsoleApplication1.Program/MyClass::set_Id(int32)
      IL_000e:  nop
      IL_000f:  ldloc.2
      IL_0010:  ldstr      "Test"
      IL_0015:  callvirt   instance void ConsoleApplication1.Program/MyClass::set_Code(string)
      IL_001a:  nop
      IL_001b:  ldloc.2
      IL_001c:  stloc.0
    
    
      IL_001d:  newobj     instance void ConsoleApplication1.Program/MyClass::.ctor()
      IL_0022:  stloc.1
      IL_0023:  ldloc.1
      IL_0024:  ldc.i4.1
      IL_0025:  callvirt   instance void ConsoleApplication1.Program/MyClass::set_Id(int32)
      IL_002a:  nop
      IL_002b:  ldloc.1
      IL_002c:  ldstr      "Test"
      IL_0031:  callvirt   instance void ConsoleApplication1.Program/MyClass::set_Code(string)
      IL_0036:  nop
    
    0 讨论(0)
  • 2020-12-04 00:45

    The second will possibly be almost certainly insignificantly faster, because there's one fewer assignment involved, logically. In the first case, the code is actually equivalent to:

    MyClass tmp = new MyClass()
    tmp.Id = 1;
    tmp.Code = "Test";
    MyClass c = tmp;
    

    It's very possible that the JIT compiler will elide these as you're declaring a new variable - it wouldn't be able to do so if you were assigning to an existing variable with an object initializer.

    EDIT: I've just tried compiling with and without optimizations turned on, and in this "new variable" case the C# compiler elides the two if it's optimizing. It doesn't otherwise (but the JIT still could). In the "reassignment" case it could make an observable difference, so I wouldn't expect the same optimization. I haven't checked though.

    I would be very surprised to see a situation where it actually made a significant difference though, so I'd go with the more readable option, which IMO is the first.

    EDIT: I thought folks might be interested in a benchmark showing it making a difference. This is deliberately horrible code to make the hidden extra assignment slow - I've created a big, mutable struct. Urgh. Anyway...

    using System;
    using System.Diagnostics;
    
    struct BigStruct
    {
        public int value;
        #pragma warning disable 0169
        decimal a1, a2, a3, a4, a5, a6, a7, a8;
        decimal b1, b2, b3, b4, b5, b6, b7, b8;
        decimal c1, c2, c3, c4, c5, c6, c7, c8;
        decimal d1, d2, d3, d4, d5, d6, d7, d8;
        #pragma warning restore 0169
    }
    
    class Test
    {
        const int Iterations = 10000000;
    
        static void Main()
        {
            Time(NewVariableObjectInitializer);
            Time(ExistingVariableObjectInitializer);
            Time(NewVariableDirectSetting);
            Time(ExistingVariableDirectSetting);
        }
    
        static void Time(Func<int> action)
        {
            Stopwatch stopwatch = Stopwatch.StartNew();
            action();
            stopwatch.Stop();
            Console.WriteLine("{0}: {1}ms",
                              action.Method.Name,
                              stopwatch.ElapsedMilliseconds);
        }
    
        static int NewVariableObjectInitializer()
        {
            int total = 0;
            for (int i = 0; i < Iterations; i++)
            {
                BigStruct b = new BigStruct { value = i };
                total += b.value;
            }
            return total;
        }
    
        static int ExistingVariableObjectInitializer()
        {
            int total = 0;
            BigStruct b;
            for (int i = 0; i < Iterations; i++)
            {
                b = new BigStruct { value = i };
                total += b.value;
            }
            return total;
        }
    
        static int NewVariableDirectSetting()
        {
            int total = 0;
            for (int i = 0; i < Iterations; i++)
            {
                BigStruct b = new BigStruct();
                b.value = i;
                total += b.value;
            }
            return total;
        }
    
        static int ExistingVariableDirectSetting()
        {
            int total = 0;
            BigStruct b;
            for (int i = 0; i < Iterations; i++)
            {
                b = new BigStruct();
                b.value = i;
                total += b.value;
            }
            return total;
        }
    }
    

    Results (with /o+ /debug-):

    NewVariableObjectInitializer: 3328ms
    ExistingVariableObjectInitializer: 3300ms
    NewVariableDirectSetting: 1464ms
    ExistingVariableDirectSetting: 1491ms
    

    I'm somewhat surprised that the NewVariableObjectInitializer version is slower than the direct setting ones... it looks like the C# compiler doesn't optimize this case in the way that it does for reference types. I suspect there's some subtlety around value types that prevents it.

    0 讨论(0)
  • 2020-12-04 00:45

    They are the same. But we all prefer the first one, it's more readable and clearer, isn't it?

    0 讨论(0)
  • 2020-12-04 00:48

    I tested by creating 100 million objects each using a parameterised constructor, a parameterless constructor with initialiser, and a parameterless constructor with setters, and there is no measurable difference at all. There was a slight difference in execution time, but running the tests in different order changed the results, so the differences are just due to the garbage collector kicking in at different times.

    Creating 100 million objects took about 1.5 seconds, so there isn't much reason to try to make it faster either.

    Personally I prefer a parameterised constructor, as I then can make the property setters private so that I can make the class immutable if I want to:

    class MyClass {
    
      public int Id { get; private set; }
      public string Code { get; private set; }
    
      public MyClass(int id, string code) {
        Id = id;
        Code = code;
      }
    
    }
    

    Also, this way you can make sure that all properties have been set properly when the object is created.

    0 讨论(0)
提交回复
热议问题