109

假设我有一Album堂课:

public class Album 
{
    public string Name {get; set;}
    public string Artist {get; set;}
    public int Year {get; set;}

    public Album()
    { }

    public Album(string name, string artist, int year)
    {
        this.Name = name;
        this.Artist = artist;
        this.Year = year;
    }
}

当我想将数据分配给类型的对象时Album,接下来的两种方法有什么区别:

通过构造函数

var albumData = new Album("Albumius", "Artistus", 2013);

实例化时

var albumData = new Album 
                    {
                         Name = "Albumius",
                         Artist = "Artistus",
                         Year = 2013
                    };
4

3 回答 3

137

两种方法都调用构造函数,它们只是调用不同的构造函数。这段代码:

var albumData = new Album 
                {
                     Name = "Albumius",
                     Artist = "Artistus",
                     Year = 2013
                };

是此等效代码的语法简写:

var albumData = new Album();
albumData.Name = "Albumius";
albumData.Artist = "Artistus";
albumData.Year = 2013;

编译后两者几乎相同(对于几乎所有意图和目的来说足够接近)。因此,如果无参数构造函数不是公开的:

public Album() { }

那么无论如何你将无法使用对象初始化器。 因此,主要问题不是在初始化对象时使用哪个,而是对象首先公开的构造函数。 如果对象公开了两个构造函数(例如您的示例中的构造函数),那么可以假设这两种方式对于构造对象同样有效。

有时对象不公开无参数构造函数,因为它们需要特定的构造值。尽管在这种情况下,您仍然可以将初始化语法用于其他值。例如,假设您的对象上有这些构造函数:

private Album() { }
public Album(string name)
{
    this.Name = name;
}

由于无参数构造函数是私有的,因此您不能使用它。但是您可以使用另一个并仍然使用初始化器语法:

var albumData = new Album("Albumius")
                {
                     Artist = "Artistus",
                     Year = 2013
                };

编译后的结果将与以下内容相同:

var albumData = new Album("Albumius");
albumData.Artist = "Artistus";
albumData.Year = 2013;
于 2013-10-02T13:45:26.993 回答
32

对象初始化器很酷,因为它们允许您内联设置类。权衡是你的类不能是不可变的。考虑:

public class Album 
{
    // Note that we make the setter 'private'
    public string Name { get; private set; }
    public string Artist { get; private set; }
    public int Year { get; private set; }

    public Album(string name, string artist, int year)
    {
        this.Name = name;
        this.Artist = artist;
        this.Year = year;
    }
}

如果以这种方式定义类,则意味着在构建类后,实际上并没有一种简单的方法来修改类的内容。不变性有好处。当某些东西是不可变的时,更容易确定它是正确的毕竟,如果它在构建后无法修改,那么它就永远不会“错误”(一旦你确定它的结构是正确的)。当您创建匿名类时,例如:

new { 
    Name = "Some Name",
    Artist = "Some Artist",
    Year = 1994
};

编译器会自动创建一个不可变类(即匿名类在构造后不能修改),因为不变性就是那么有用。出于这个原因,大多数 C++/Java 样式指南经常鼓励创建成员const(C++) 或(Java)。final当活动部件较少时,更大的应用程序更容易验证。

话虽如此,在某些情况下,您希望能够快速修改类的结构。假设我有一个要设置的工具:

public void Configure(ConfigurationSetup setup);

我有一个有许多成员的班级,例如:

class ConfigurationSetup {
    public String Name { get; set; }
    public String Location { get; set; }
    public Int32 Size { get; set; }
    public DateTime Time { get; set; }

    // ... and some other configuration stuff... 
}

当我想配置一些属性组合时,使用对象初始值设定项语法很有用,但不一定要同时配置所有属性。例如,如果我只想配置Nameand Location,我可以这样做:

ConfigurationSetup setup = new ConfigurationSetup {
    Name = "Some Name",
    Location = "San Jose"
};

这允许我设置一些组合,而不必为每个可能的排列定义一个新的构造函数。

总的来说,我认为让你的类不可变从长远来看会为你节省大量的开发时间,但是拥有对象初始化器语法可以更容易地设置某些配置排列。

于 2013-10-02T13:45:46.110 回答
13

第二种方法是C# 中的对象初始化器

对象初始值设定项允许您在创建时将值分配给对象的任何可访问字段或属性,而无需显式调用构造函数

第一种方法

var albumData = new Album("Albumius", "Artistus", 2013);

显式调用构造函数,而在第二种方法中,构造函数调用是隐式的。使用对象初始化器,您也可以省略一些属性。像:

 var albumData = new Album
        {
            Name = "Albumius",
        };

对象初始化器将转换为:

var albumData; 
var temp = new Album();
temp.Name = "Albumius";
temp.Artist = "Artistus";
temp.Year = 2013;
albumData = temp;

Jon Skeet在这里回答了为什么它使用临时对象(在调试模式下) 。

就这两种方法的优点而言,如果您不想初始化所有字段,IMO 对象初始化程序会更容易使用。就性能差异而言,我认为不会有任何差异,因为对象初始值设定项会调用无参数构造函数,然后分配属性。即使会有性能差异,也应该可以忽略不计。

于 2013-10-02T13:37:15.347 回答