使用初始化程序块是不好的

时间:2010-04-04 14:34:52

标签: c# coding-style initialization

您好我在C#中使用初始化程序块

new Something { foo = 1, bar = 2 };

但人们说这是不好的做法。

我不认为这是错的,是吗?

7 个答案:

答案 0 :(得分:9)

你需要问问自己,你的类型是否应该是可变的。就个人而言,我喜欢不可变类型 - 它们可以更容易地推断出正在发生的事情,更容易验证(一旦调用构造函数并验证状态,你知道它不会变得无效)并且它们对于并发性很好。

另一方面,对象初始化器在 合理具有可变类型的情况下肯定是有用的。例如,ProcessStartInfo有效地用作Process的构建器类型。能写的很有用:

var info = new ProcessStartInfo { 
    FileName = "notepad.exe",
    Arguments = "foo.txt",
    ErrorDialog = true
};
Process process = Process.Start(info);

实际上,你甚至可以做所有这些内联而不是额外的变量。 My Protocol Buffers端口使用相同的模式:

Foo foo = new Foo.Builder { 
    FirstProperty = "first",
    SecondProperty = "second"
}.Build();

现在,构建器模式的一个替代方法是构造函数参数(可能通过工厂方法)。这个历史的缺点是你需要不同的重载取决于设置的属性,如果几个参数具有相同的类型,很难分辨哪个是哪个。使用可选参数和命名参数,C#4使这变得更加容易。例如,如果您要构建一个电子邮件类,则可以:

Email email = new Email(
    from: "skeet@pobox.com",
    to: "jon@example.com",
    subject: "Test email",
    body: textVariable
);

在清晰度方面,它具有许多与对象初始化器相同的好处,但没有可变性惩罚。上面的构造函数调用可能错过了一些可选参数,例如附件和BCC列表。我认为对于我们这些喜欢不变性的人来说,这将是C#4的最大好处之一,同时也是对象初始化者的清晰度。

答案 1 :(得分:8)

使用初始化块代替相应的构造函数重载(如果存在的话)是有问题的(我不会说“坏”)。

public class Entity
{
    public Entity()
    {
    }

    public Entity(int id, string name)
    {
        this.ID = id;
        this.Name = name;
    }

    public int ID { get; set; }
    public string Name { get; set; }
}

如果你有这个非常简单的类,那么通常最好写:

var entity = new Entity(1, "Fred");

......而不是写:

var entity = new Entity { ID = 1, Name = "Fred" };

至少有两个很好的理由:

  1. 您不确切知道构造函数正在做什么。在某些情况下,构造对象和然后设置公共属性而不是通过构造函数本身传递值可能会显得更加昂贵。 (您可能知道情况并非如此,但作为一个类的消费者,您不应该假设知道对实现细节的关注,因为它们可能会发生变化)。

  2. 如果其中一个或多个属性的名称已更改,或者变为只读(ID可能应该首先出现,但可能不是',那么您的代码不会中断t由于像ORM那样的架构限制。

  3. 但是,有一种情况是 使用初始化程序而不是重载的构造函数,这就是链接在Linq to SQL / EF查询中选择的时候:

    var bars =
        from f in ctx.Foo
        select new Bar { X = f.X, Y = f.Y };
    var bazzes =
        from b in bars
        select new Baz { ... };
    

    如果使用构造函数重载而不是默认构造函数+初始化程序,则实际上可能会失败并显示“不支持映射”。然而,这是所使用的技术的约束(并且是不受欢迎的技术),而不是编码风格问题。

    其他情况下,您应该更喜欢构造函数重载初始化程序。

    如果没有有用的/相关的构造函数重载可以做与初始化程序相同的事情,那么继续编写初始化程序,它没有任何问题。该功能存在是有充分理由的 - 它使代码更容易编写读取。

答案 2 :(得分:4)

  

但人们说这是不好的做法。

谁说这个?至少,这是一个有争议的声明。

目前似乎风靡一时,许多着名的C#博客广泛使用它。

使用构造函数的优点是它更具可读性,因为代码清楚地显示哪些属性被赋值为什么值。比较:

var x = new Something { Foo = 1, Bar = 2 };

var x = new Something(1, 2);

此外,如果没有合适的构造函数,代码比手动分配属性更简洁:

var x = new Something();
x.Foo = 1;
x.Bar = 2;

就个人而言,我更喜欢不可变对象(即一旦创建,无法更改的对象)。不幸的是,初始化程序块不能与这些对象一起使用(目前),因为为了使这个模式工作,对象必须具有属性设置器,而不可变对象不具有该属性设置器。 p>

但只要使用的对象不是不可变的,我就没有看到使用初始化符号的令人信服的理由。

答案 3 :(得分:3)

初始化程序块是一种很好的做法,原因如下:

  1. 您可以在获取其引用

    之前创建一个对象并覆盖其属性
    this.ObjA = new ObjA
    {
        Age = 20,
        Name = "123",
    };
    
    // this.ObjA will not be set until properties have all been defined
    // - safer when multithreading
    
  2. 无参数构造函数仍然可以在场景后面执行操作 (例如,初始化州成员)。

  3. 您可以与带参数的构造函数一起使用

    this.ObjA = new ObjA(20)
    {
        Name = "123",
    };
    
  4. 使用无参数构造函数更适合(反)序列化方案

    您可以创建各种对象,通过GUI更改其状态,序列化它们,在其他地方反序列化它们。

  5. 这种做法迫使作者编写更强大的代码 - 在每次更改类的元数据时,执行操作的顺序不会轻易导致应用程序崩溃。

答案 4 :(得分:1)

初始化程序块没有任何问题,但是如果你的类型有很多属性,并且只需要在每个实例上设置几个属性,那么你应该在构造函数中使它们成为必需。

您的类的用户将知道他们无法在未指定这些值的情况下创建对象。

答案 5 :(得分:0)

我认为这很好。

因为它会减少你的输入次数

答案 6 :(得分:0)

对象工作必不可少的属性应该在构造函数中初始化,因此你应该在contstructor中提供适当的参数。

初始化程序块对于C#3.0的几个新功能非常方便,但是你应该记住,它们不是用来替换构造函数中的参数。