【问题标题】:c# constructors vs auto-properties and object initializersc# 构造函数 vs 自动属性和对象初始值设定项
【发布时间】:2011-04-08 02:18:27
【问题描述】:

我经常使用自动属性,但我越来越远离设置具有在构造函数中初始化的只读支持字段的类。我删除了所有设置器,并且仅在属性明确需要设置器时才添加后面。

我发现这让我的课程更加健壮和优雅,OO 明智,我为自己没有早点这样做而自责。

我发现构造函数在 c# 代码示例中通常没有得到充分利用,我认为自动属性和对象初始化器是其中很大一部分,所以我的问题是为什么 c# 团队会推动这样的特性,而不是更多地关注交付具有更多推动最佳实践的功能。一般来说,我认为编写糟糕的代码太容易了,并且相信可以做更多的事情来帮助程序员编写好代码

【问题讨论】:

  • 并非所有类型都必须是不可变的。在构造函数中采用可变类型,然后将其复制到私有成员是非常常见的(也是有益的)。然后通过外部代码屏蔽它的变化(假设它对任何可变依赖项做同样的事情)。对于普通的旧数据对象尤其如此。

标签: c# automatic-properties object-initializers


【解决方案1】:

从对话中,我相信 C# 团队明白,他们使编写可变类型变得更容易,但没有为不可变类型提供类似的好处。并不是说随着时间的推移它们使不可变性变得更加困难——它们只是没有让它变得更容易……除了匿名类型,它们是不可变的,但还有其他各种缺点。我当然不希望自动属性被带走——在合适的地方,它们真的很有用。我只想拥有 readonly 属性的等效项(允许仅在构造函数中设置它们)。

我发现 C# 4 的命名参数和可选参数使得构造不可变类型实例变得更加容易 - 您仍然可以获得对象初始化器的许多好处,而没有可变性的缺点。只需为您的类型中真正可选的方面提供默认值,将其余部分保留为强制构造函数参数,调用者可以做他们想做的事 - 使用命名参数来增加清晰度。

不幸的是,集合初始化器更难破解。我希望看到可以与不可变集合一起使用的“链接”初始化程序,这样编译器就可以创建对链接在一起的Plus 的调用,而不是在同一个实例上重复调用Add

ImmutableList<string> x = new ImmutableList<string> { "a", "b", "c" };

会去:

ImmutableList<string> x = new ImmutableList<string>().Plus("a")
                                                     .Plus("b")
                                                     .Plus"(c");

当然,在框架中拥有更多不可变集合会很好:)

当然,这对自动道具方面没有帮助。我不得不承认我最近一直在作弊,使用私有 setter 假装不变性:

public string Name { get; private set; }

这确实让我觉得自己很肮脏,当我真正的意图时,它并没有使其真正不可变。

基本上,我是说我能感受到你的痛苦——我很确定 C# 团队能感受到。但请记住,他们的资源有限,而且设计语言非常困难。

您可能会发现 videos from NDC 2010 很有趣 - 与 Eric Lippert、Mads Torgersen、Neal Gafter(和我)进行了精彩的小组讨论,我对 C# 5 的建议在另一个视频中。

【讨论】:

  • 如果不可变性变得更容易,那肯定会很好。为了保证完全不变性,您必须完全控制您包含的所有类。例如,您说匿名类型是不可变的,但是我向它添加了一个数组和一个可变列表,并且能够修改它们。如果您有一个今天不可变的可公开访问的成员,那么明天就可以将其变为可变的,结果您的不可变性就会被破坏。
  • 谢谢,我会看看那些视频。在阅读了更多内容之后,似乎 F# 正在以更好的方式处理这些事情,默认情况下锁定对象并在需要时允许可变性。我猜想 legacy 阻止了 c# 团队朝那个方向前进。您对 F# 与 C# Jon 有何看法?
  • @TT:我在 F# 方面的经验并不如我所愿,但我确实喜欢 F# 所做的各种事情。我对 C# 5 提出的许多建议都是基于 F#。
  • 我 100% 同意你关于私人二传手的看法。对我来说,去propg+tab+tab 而不是创建一个适当的只读支持字段太容易了。 Surly public string Name { get; readonly set; }(听起来很奇怪)会进入 C# 5 吗?
  • @Alex:这正是我之前提出的 :) 不过我不知道它是否会进入 C# 5。希望我们很快就会开始看到第一部分。
【解决方案2】:

我删除了所有的 setter,只在属性明确需要 setter 时才添加后面。 我发现这使我的类在面向对象方面更加健壮和优雅

我完全同意你的看法。我遇到了遗留代码,其中有很多用于某些类层次结构的对象初始值设定项。我需要添加一些属性,然后发现所有构造类实例的位置让我头疼。 我第一次提交。现在我需要再添加一个属性。这太疯狂了!

为了限制对象初始化器的使用,我删除了无参数构造函数。

【讨论】:

    【解决方案3】:

    我发现构造函数在 c# 代码示例中通常未被充分利用,我认为自动属性和对象初始化器是其中的重要组成部分

    如果您的对象有很多属性,您显然不想从构造函数中全部初始化它们。必须传递超过 4 或 5 个参数对可读性非常不利(尽管 Intellisense 使其易于编写)。此外,如果您只想初始化几个属性并为其他属性使用默认值,则需要许多构造函数重载,或者必须将这些默认值显式传递给构造函数。

    在这种情况下,对象初始化器非常方便,只要属性不是只读的(但正如 Jon 指出的,C# 4 中的可选参数是一个不错的选择)

    为什么 c# 团队会推动这样的功能,而不是更多地专注于交付推动最佳实践的功能

    我认为引入对象初始化器是因为它们对于 Linq 是必需的:没有它们就无法创建匿名类型。至于自动属性,它们不那么重要,但它可能很容易实现,并且对于只封装字段的属性来说,它可以节省大量时间。

    【讨论】: