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

Ano*_*non 11 c# coding-style initialization

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

new Something { foo = 1, bar = 2 };
Run Code Online (Sandbox Code Playgroud)

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

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

Jon*_*eet 9

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

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

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

实际上,你甚至可以做所有这些内联而不是额外的变量.我的协议缓冲区端口使用相同的模式:

Foo foo = new Foo.Builder { 
    FirstProperty = "first",
    SecondProperty = "second"
}.Build();
Run Code Online (Sandbox Code Playgroud)

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

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

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

  • 有趣的是; SO语法高亮显示器认为'from'是一个关键字(可能来自支持突出显示LINQ.) (2认同)

Aar*_*ght 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; }
}
Run Code Online (Sandbox Code Playgroud)

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

var entity = new Entity(1, "Fred");
Run Code Online (Sandbox Code Playgroud)

......而不是写:

var entity = new Entity { ID = 1, Name = "Fred" };
Run Code Online (Sandbox Code Playgroud)

至少有两个很好的理由:

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

  2. 如果其中一个或多个属性的名称已更改,或者变为只读(ID可能应该首先出现,但可能不是由于ORM的体系结构约束),您的代码将不会中断.

但是,有一种情况是您必须使用初始化程序而不是重载的构造函数,这就是链接在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 { ... };
Run Code Online (Sandbox Code Playgroud)

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

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

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


Kon*_*lph 3

\n

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

\n
\n\n

这是谁说的?至少,\xe2\x80\x99 是一个有争议的说法。

\n\n

它目前似乎很流行,许多著名的 C# 博客都广泛使用它。

\n\n

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

\n\n
var x = new Something { Foo = 1, Bar = 2 };\n
Run Code Online (Sandbox Code Playgroud)\n\n

\n\n
var x = new Something(1, 2);\n
Run Code Online (Sandbox Code Playgroud)\n\n

此外,如果不存在适当的构造函数,则代码比手动分配给属性更简洁:

\n\n
var x = new Something();\nx.Foo = 1;\nx.Bar = 2;\n
Run Code Online (Sandbox Code Playgroud)\n\n

就我个人而言,我更喜欢不可变对象(即一旦创建就无法更改的对象)。不幸的是,初始化程序块不能与此类对象结合使用(目前),因为要使此模式起作用,对象必须具有属性设置器,而不可变对象\xe2\x80\x99没有属性设置器。

\n\n

但只要使用的对象是\xe2\x80\x99t 不可变的,我就不\xe2\x80\x99t 看到反对使用初始值设定项符号的令人信服的理由。

\n