C# 使用初始化程序块不好吗

C# 使用初始化程序块不好吗,c#,coding-style,initialization,C#,Coding Style,Initialization,嗨,我在C中使用初始值设定项块# 但人们说这是一种不好的做法 我不认为这是错的,是吗 但人们说这是一种坏习惯 这是谁说的?至少,这是一个有争议的声明 目前它似乎风靡一时,许多著名的C#blog都广泛使用它 与使用构造函数相比,它的优点是可读性更强,因为代码清楚地显示了哪些属性被分配了哪些值。比较: var x = new Something { Foo = 1, Bar = 2 }; 与 此外,如果没有合适的构造函数,则代码比手动指定属性更简洁: var x = new Something()

嗨,我在C中使用初始值设定项块#

但人们说这是一种不好的做法

我不认为这是错的,是吗

但人们说这是一种坏习惯

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

目前它似乎风靡一时,许多著名的C#blog都广泛使用它

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

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

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

var x = new Something();
x.Foo = 1;
x.Bar = 2;
就个人而言,我更喜欢不可变的对象(即一旦创建就无法更改的对象)。不幸的是,初始化程序块不能与这些对象结合使用(目前),因为要使这种模式工作,对象必须有属性设置器,而不可变对象没有属性设置器

但只要所使用的对象不是不可变的,我就看不出有什么令人信服的理由反对使用初始值设定符号。

我认为这是好的


因为它大大减少了输入

初始值设定项块没有问题,但是如果您的类型(例如)有许多属性,并且每个实例上只需要设置几个属性,那么您应该在构造函数中要求它们


类的用户将知道,如果不指定这些值,他们将无法创建对象

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

  • 在获取对象的引用之前,您可以创建对象并覆盖其属性

    this.ObjA = new ObjA
    {
        Age = 20,
        Name = "123",
    };
    
    // this.ObjA will not be set until properties have all been defined
    // - safer when multithreading
    
  • 无参数构造函数仍然可以在幕后进行操作 (例如,初始化国家成员)

  • 可以与带参数的构造函数结合使用

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

    您可以创建各种对象,通过GUI更改它们的状态,序列化它们,在别处反序列化它们

  • 这种做法迫使作者编写更健壮的代码——在这种情况下,每次更改类的元数据时,执行操作的顺序都不会轻易导致应用程序崩溃

  • 使用初始化块代替适当的构造函数重载(如果存在的话)是值得怀疑的(我不会说“不好”)

    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" };
    
    var info = new ProcessStartInfo { 
        FileName = "notepad.exe",
        Arguments = "foo.txt",
        ErrorDialog = true
    };
    Process process = Process.Start(info);
    
    …而不是写:

    var entity = new Entity(1, "Fred");
    
    var entity = new Entity { ID = 1, Name = "Fred" };
    
    var info = new ProcessStartInfo { 
        FileName = "notepad.exe",
        Arguments = "foo.txt",
        ErrorDialog = true
    };
    Process process = Process.Start(info);
    
    这至少有两个很好的理由:

  • 你不知道构造器到底在做什么。在某些情况下,与通过构造函数本身传递值相比,构建对象然后设置公共属性的成本可能要高得多。(您可能知道情况并非如此,但作为类的使用者,您不应该假定知道实现细节,因为它们可能会发生更改)

  • 如果这些属性中的一个或多个的名称发生更改,或者成为只读属性,那么您的代码就不会中断(ID
    ID
    可能本来应该是只读的,但可能不是由于ORM之类的体系结构约束)

  • 但是,在一种情况下,必须使用初始值设定项而不是重载构造函数,即在Linq到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 { ... };
    
    如果使用构造函数重载而不是默认构造函数+初始值设定项,则这实际上可能会因“不支持映射”而失败。然而,这是所使用技术的一个限制(这是一个不受欢迎的限制),而不是编码风格的问题

    其他情况下,您应该更喜欢构造函数重载而不是初始值设定项


    如果没有有用的/相关的构造函数重载可以做与初始值设定项相同的事情,那么继续编写初始值设定项,它没有问题。该功能的存在有一个很好的理由-它使代码更易于编写和读取。

    对于对象工作至关重要的属性应该在构造函数中初始化,因此您应该在contstructor中提供适当的参数


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

    您需要问问自己,您的类型是否应该是可变的。就我个人而言,我喜欢不可变类型——它们使我们更容易对正在发生的事情进行推理,更容易进行验证(一旦调用了构造函数并验证了状态,您就知道它不会变得无效),而且它们对于并发性非常好

    另一方面,对象初始值设定项在具有可变类型的情况下当然很有用。例如,
    ProcessStartInfo
    有效地用作
    Process
    的生成器类型。能够写以下内容很有用:

    var entity = new Entity(1, "Fred");
    
    var entity = new Entity { ID = 1, Name = "Fred" };
    
    var info = new ProcessStartInfo { 
        FileName = "notepad.exe",
        Arguments = "foo.txt",
        ErrorDialog = true
    };
    Process process = Process.Start(info);
    
    实际上,您甚至可以内联完成所有这些操作,而无需使用额外的变量。我的协议缓冲区端口使用相同类型的模式:

    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
    );
    
    在清晰性方面,这与对象初始值设定项有许多相同的优点,但没有可变性惩罚。上面的构造函数调用可能遗漏了一些可选参数,例如附件和密件抄送列表。我认为这将证明是C#4对我们这些喜欢它的人的最大好处之一