C#:通过构造函数与实例化将数据分配给属性

Posted

技术标签:

【中文标题】C#:通过构造函数与实例化将数据分配给属性【英文标题】:C# : assign data to properties via constructor vs. instantiating 【发布时间】:2013-10-08 21:53:22 【问题描述】:

假设我有一个 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
                    ;

【问题讨论】:

What's the difference between an object initializer and a constructor? 的可能重复项 对于构造函数中的一个,您可以将值分配给私有成员变量,而使用对象初始化器,您只能使用公共属性 您的构造函数强制您传递姓名、艺术家和年份。它们对于您的对象启动器是可选的。 如果你的构造函数有一些逻辑来构造你的对象并且它需要一些参数来构造你的对象,那么对象初始化器可能不适合你的需要。但在“您显示的”情况下并没有什么不同。对象初始化器实际上是为 Linq 中的未命名对象完成的,因此可以创建原子对象。基本上,答案是,有时不一样,有时不一样。 如果你有struct,直接赋值属性比调用构造函数快得多。确保你的结构是不可变的。 【参考方案1】:

第二种方法是object initializer in 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 here 回答。

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

【讨论】:

我认为部分问题是 - “绕过构造函数是一件好事吗?”。是否有好处(灵活性)或缺点(绕过构造函数逻辑,也许是速度?) 构造函数没有被绕过。将使用无参数构造函数。 是的,这就是我真正想要的,意思是……比另一个有优势吗?有没有一个比另一个有更好的性能?类似的东西(缺点和优点) 请记住,所有没有显式构造函数的对象都会自动实现默认的无参数构造函数。如果你包含一个有参数的构造函数,那么你必须显式地实现一个无参数的构造函数。如果您在没有默认构造函数的情况下尝试使用上述语法(即new Album params ),则会导致编译时错误——本质上您总是在调用构造函数,而对象初始化器只是用于设置属性/字段的语法糖实例化后 @guymid ... 和参数化构造函数执行可以通过调用无参数构造函数简单地绕过的逻辑将是代码异味。【参考方案2】:

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

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;

【讨论】:

关于前两个例子;有趣的是,如果对创建对象的引用存储在字段或全局而不是局部变量中,则编译后这些将不相同。当属性设置器中发生异常时,在一种情况下该字段将被设置,而在另一种情况下则不会。在处理一次性物品和 CA2000 时,这类事情可能很重要。 这可能会迟到,但是,请查看由这个答案引起的this question。你应该修复这个答案【参考方案3】:

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

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++) 或 final (Java)。当活动部件较少时,更大的应用程序更容易验证。

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

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

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

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

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

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

【讨论】:

@siercodesalot,谢谢您的回答。但我不认为只指定对象初始化器中的一些属性是最好的论点。因为,就像那样,在构造函数中您可以使用默认值并跳过指定属性。 看看AppDomainSetup (msdn.microsoft.com/en-us/library/system.appdomainsetup.aspx),它是用来配置新的应用程序域的。你真的想要一个有 20 种不同属性的构造函数——尤其是当你只想实际定义一两个属性的时候?其次,如果您只想在构造函数列表的末尾(而不是开头)配置属性,则默认值实际上不起作用。 不要误会我的意思,我对对象初始化器非常熟悉,它是有用且快速的方法。我想知道是否有任何区别,例如性能或最佳实践,或利弊/利弊。您可以在构造函数中显式指定要定义的参数。例如,假设我有所有具有指定默认值的构造函数参数,我可以按照我的意愿在列表中重新排列它们:var album = new Album(year: 2013, artist: "Artistus)。当然,这是更不方便的工作。或者你的意思是结束/开始? 这是一个很好的观点,虽然您可以重新排列参数的顺序,但在某个时刻,构造函数会变得很长,以至于当您选择只使用对象初始化器。也就是说, 使用对象初始化器的主要原因实际上归结为您希望通过构造函数强制执行不变量的情况。例如,不可能使用对象初始化进行函数式(不可变)编程,因为您的属性设置器必须是公共的。如果有一个主要的权衡,可能就是不变性。 如果类是匿名的,你说“编译器会自动创建一个不可变的类”。但是假设我用匿名类填充了一个列表,那么我仍然可以引用它们并更改它们的属性,对吗?

以上是关于C#:通过构造函数与实例化将数据分配给属性的主要内容,如果未能解决你的问题,请参考以下文章

自动属性无法在 C# 实例构造函数中初始化

Swift学习笔记九:类 Class

C#图解教程 第六章 深入理解类

C# 构造函数总结

C#中静态字段声明时赋值与构造函数中赋值

C# 构造函数总结