只读字段 vs 只读属性

5
有时我会看到类定义中定义了只读成员,如下所示:
class Foo
{
    private readonly string bar;

    public Foo(string bar)
    {
        this.bar = bar;
    }

    public string Bar => bar;
}

有时我会看到像这样定义只读成员的类:

and other times I see classes defined with readonly members like so:

class Foo
{
    public Foo(string bar)
    {
        Bar = bar;
    }

    public string Bar { get; private set; }
}

第二个例子对我来说看起来更易读/简洁,但我想知道有没有正当理由明确定义只读的后备字段(如第一个例子)?
赞赏深入分析两个示例,最好涵盖在CLR中真正发生的事情。

16
实际上,第二个版本非常不同。一个等价的版本是public string Bar {get;}。你发表的例子public string Bar { get; private set; }并不是只读的,它只是不能在公共接口中设置。 - Scott Chamberlain
是的,我正在使用C# 5。我想这就解释了。 - user1620220
@user1620220 完全正确。我想是时候升级了 :-) - Matthew Layton
1
如果你真的喜欢第二个,那么你可以通过简单地删除setter来使其完全等同于第一个,例如public string Bar { get; }。这是另一个C#6的改进。 - Ivan Stoev
第二个选项实际上不是只读的 - 它是可变的,因为值可以在任何时候从所有者实例更改。 - IAmJersh
显示剩余3条评论
1个回答

9
第一种是简写方式。
class Foo
{
    private readonly string bar;
    public Foo(string bar)
    {
        this.bar = bar;
    }
    public string get_Bar() { return this.bar; }
}

一个拥有getter方法的属性,本质上就是一个简写的getter函数。

第二种方式则是一种简写的写法。

class Foo
{
  private string __bar;
  public Foo(string bar)
  {
      this.set_Bar(bar);
  }
  public string get_Bar() { return this.__bar; }
  private void set_Bar(string b) { this.__bar = b; }
}

再次强调,属性只是在后台有一对get/set方法。

我想知道是否有正当理由明确定义一个只读的备份字段。

这两种形式都是完全合法的,选择你更喜欢的那个。

需要注意的是这两种形式并不等价。在第一种情况下,属性的备份字段只能在构造函数中更改。而在后一种情况下,属性的备份字段可以在类中的任何位置更改。


反射对它们有任何影响吗? - Matthew Layton
@series0ne:我不明白这个问题。 - Eric Lippert
嗯,可能我不能在第一个示例上调用设置器,因为它没有一个,而且不管它是否只读,反射都会让我随意地更改该值,这是合理的假设。 - Matthew Layton
1
@series0ne:如果你的组件获得了私有反射权限,那么你的组件可以随心所欲地做任何事情。完全信任意味着完全信任,你可以打破规则,并对此负责理解其全部后果。如果你感觉使用私有反射权限来操纵实现细节的值,那是你的权利。 - Eric Lippert
最后一个段落不是决定使用什么的唯一要考虑的因素吗?如果你想在构造函数外部更改值,就不要使用只读后备字段,否则保持只读,或者我漏掉了什么? - Nope
为了完整起见,您能否解释一下新功能(只读)自动实现属性而不使用set以及具有支持字段的等效功能? - Second Person Shooter

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接