通过对象初始化设置属性与否:任何区别?

Phi*_*oie 35 c# setter performance properties object-initialization

这是一个简单的问题:这之间是否存在任何(性能)​​差异:

Person person = new Person()
{
  Name = "Philippe",
  Mail = "phil@phil.com",
};
Run Code Online (Sandbox Code Playgroud)

还有这个

Person person = new Person();
person.Name = "Philippe";
person.Mail = "phil@phil.com";
Run Code Online (Sandbox Code Playgroud)

您可以想象具有更多属性的更大对象.

Mar*_*ers 43

它们几乎完全等效,只是第一种方法(使用对象初始化程序)仅适用于C#3.0及更高版本.任何表现差异都很小,不值得担心.

它们产生几乎相同的IL代码.第一个给出了这个:

.method private hidebysig instance void ObjectInitializer() cil managed
{
    .maxstack 2
    .locals init (
        [0] class Person person,
        [1] class Person <>g__initLocal0)
    L_0000: newobj instance void Person::.ctor()
    L_0005: stloc.1 
    L_0006: ldloc.1 
    L_0007: ldstr "Philippe"
    L_000c: callvirt instance void Person::set_Name(string)
    L_0011: ldloc.1 
    L_0012: ldstr "phil@phil.com"
    L_0017: callvirt instance void Person::set_Mail(string)
    L_001c: ldloc.1 
    L_001d: stloc.0 
    L_001e: ldloc.0 
    L_001f: callvirt instance string [mscorlib]System.Object::ToString()
    L_0024: pop 
    L_0025: ret 
}
Run Code Online (Sandbox Code Playgroud)

第二个给出了这个:

.method private hidebysig instance void SetProperties() cil managed
{
    .maxstack 2
    .locals init (
        [0] class Person person)
    L_0000: newobj instance void Person::.ctor()
    L_0005: stloc.0 
    L_0006: ldloc.0 
    L_0007: ldstr "Philippe"
    L_000c: callvirt instance void Person::set_Name(string)
    L_0011: ldloc.0 
    L_0012: ldstr "phil@phil.com"
    L_0017: callvirt instance void Person::set_Mail(string)
    L_001c: ldloc.0 
    L_001d: callvirt instance string [mscorlib]System.Object::ToString()
    L_0022: pop 
    L_0023: ret 
}
Run Code Online (Sandbox Code Playgroud)

如您所见,生成了几乎相同的代码.请参阅下面的我编译的确切C#代码.

性能测量显示非常相似的结果,使用对象初始化程序语法的性能提升非常小:

Method               Iterations per second
ObjectInitializer    8.8 million
SetProperties        8.6 million

我用来测试性能的代码:

using System;

class Person
{
    public string Name { get; set; }
    public string Mail { get; set; }
}

class Program
{
    private void ObjectInitializer()
    {
        Person person = new Person()
        {
            Name = "Philippe",
            Mail = "phil@phil.com",
        };
        person.ToString();
    }

    private void SetProperties()
    {
        Person person = new Person();
        person.Name = "Philippe";
        person.Mail = "phil@phil.com";
        person.ToString();
    }

    private const int repetitions = 100000000;

    private void Time(Action action)
    {
        DateTime start = DateTime.UtcNow;
        for (int i = 0; i < repetitions; ++i)
        {
            action();
        }
        DateTime end = DateTime.UtcNow;
        Console.WriteLine(repetitions / (end - start).TotalSeconds);
    }

    private void Run()
    {
        Time(ObjectInitializer);
        Time(SetProperties);
        Console.WriteLine("Finished");
        Console.ReadLine();
    }

    private static void Main()
    {
        new Program().Run();
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 个人观点:对于简单的对象或仅用静态值初始化,如本例所示,初始化程序块是正常的 - 干净阅读.如果我必须分配很多属性或使用变量,我倾向于使用标准赋值选项.简单地说,如果它因OI块内的任何原因(null对象等)而失败,那么就很难弄清楚它在哪里失败了.在标准分配的情况下,它会告诉您问题的确切位置. (6认同)

Mik*_*fer 9

值得注意的另一件事是:

如果您在构造函数中未能处理异常,则会收到TypeInitializationException.虽然这看起来可能并不那么糟糕,但事实是它隐藏了问题的真正原因,并且使得追踪变得更加困难.

另一方面,如果使用对象初始值设定项,则在构造函数之外单独调用每个属性,并且任何抛出的异常都将非常明确且非常明显:它们不会被TypeInitializationException屏蔽.

通常,在构造函数中抛出异常是个坏主意.如果您想避免这种情况,请使用初始化程序.