37

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

Person person = new Person()
{
  Name = "Philippe",
  Mail = "phil@phil.com",
};

和这个

Person person = new Person();
person.Name = "Philippe";
person.Mail = "phil@phil.com";

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

4

5 回答 5

44

它们几乎完全相同,除了第一种方法(使用对象初始化器)仅适用于 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 
}

第二个给出了这个:

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

如您所见,生成了几乎相同的代码。有关我编译的确切 C# 代码,请参见下文。

性能测量显示非常相似的结果,使用对象初始化器语法的性能改进非常小:

每秒方法迭代次数
ObjectInitializer 880 万
SetProperties 860 万

我用于测试性能的代码:

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();
    }
}
于 2011-02-11T20:03:54.460 回答
10

还有一点值得注意的是:

如果您未能在构造函数中处理异常,您将获得 TypeInitializationException。虽然这可能看起来并不那么糟糕,但事实是它隐藏了问题的真正原因,并使其更难追查。

另一方面,如果您使用对象初始化程序,您将在构造函数之外单独调用每个属性,并且任何抛出的异常都将非常清晰且非常明显:它们不会被 TypeInitializationException 掩盖。

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

于 2011-02-11T21:13:16.950 回答
1

正如其他人所说,不,没有区别。请注意,第一个示例实际上并未对这些参数使用构造函数。它使用了 C# 3.0 中引入的“对象初始化器”语言特性。被调用的构造函数是默认的无参数构造函数,就像第二个示例一样。

这两个示例实际上编译为(几乎)相同的 IL 代码,并且出于所有意图和目的都做同样的事情。第一个示例只是一个相对较新的语法,用于以更简单和更具表现力的方式完成任务<opinion></opinion>。

于 2011-02-11T20:11:49.997 回答
0

不。第一种方法是 .NET 3.5 中的新方法,但第二个示例适用于以前版本的 C#。

于 2011-02-11T20:04:02.947 回答
0

正如其他回复所显示的那样,在性能方面没有显着差异。

但是,在我看来,使用带有 2 个参数的初始化程序创建一个对象就像您向使用它的任何人陈述您的意图,形成“合同”说:“这两个参数是类功能的最小值”(尽管表达该意图的正确方法是使用构造函数)。

我倾向于以这种方式考虑初始化器语法,尽管它或多或少只是语法糖。我在代码中混合使用了这两种语法。但话又说回来,那是我的个人风格。

于 2015-08-09T10:20:56.427 回答