省略setter方法和私有setter方法有什么区别?

60

省略了setter的属性和私有setter的属性之间有什么区别?

public string Foo { get; private set; }

对比

public string Foo { get; }

7
简单来说,如果一个属性没有setter(设置器),那么它就没有setter。这意味着你不能在属性声明或构造函数之外重新赋值该属性。 - BoltClock
3
重复警告:https://dev59.com/1W865IYBdhLWcg3wYNZS,https://dev59.com/RW445IYBdhLWcg3wRoPH。这是两个关于Java和C#中getter,setter和属性的最佳实践的stackoverflow问题链接。请注意,这些链接可能包含一些技术语言和术语。 - Tushar
@TusharGupta 这个被标记为C#-6.0的。 - Thomas Ayoub
1
所选择的重复内容实际上解释了新语法的预期目标,虽然它包含旧文本和新的 C# 6 后缀文本,但仍然回答了问题,即真正不可变和只读支持字段。 - Lasse V. Karlsen
5个回答

77

C# 6 中,只有 getter 的属性仅可以从构造函数中进行设置,在其它任何地方都只能用于读取。

一个带有 private set; 的属性则可以在该类的任何位置进行设置。


我明白了。我想知道为什么存在省略setter的功能。在类内部,将属性声明为只能由构造函数设置,而不能由方法设置,这样做的原因是什么? - Fred
如何防止在该方法的内部意外设置该属性? - Patrick Hofman
1
你可以在类的其他地方更新属性,但不能重新分配它。例如,如果该属性是一个列表,即使没有声明setter,你也可以在类中的任何地方调用Clear()和Add()方法。 - kilgoretrout
@Fred 因为有时您不希望在构造函数中初始设置后更改属性引用。您可以使用私有数据成员以及 readonly 关键字实现此目的:private readonly string _name;public string Name { get; } 的私有类比。 (顺便说一下:public readonly Name { get; } 是冗余的,甚至编译器也不允许这样做。) - JohnB

12

如果您在类之外使用此语法,它不会改变任何内容:

public string Foo { get; }

但是您将无法在类内更新Foo,除非在构造函数中这么做,为此,您需要使用私有设置器:

public string Foo { get; private set; }

1
“你不能在类中更新Foo。”是错误的。你只能在构造函数中设置它。 - Patrick Hofman
@PatrickHofman 感谢您的评论,我之前并不知道这一点! - Thomas Ayoub
有趣的是,每个答案都在这个特定部分上犯了错误,但仍然得到了赞同,而我的一开始就指出了这一点,却没有获得任何投票... - Patrick Hofman
1
@PatrickHofman 的点赞通常是不感恩的,一些点赞者甚至在点赞前都没有花时间阅读整个答案。 - Thomas Ayoub
似乎我无法将属性声明为“readonly”。编译器会抱怨“修饰符'readonly'对于此项无效”。 - Fred
因为它不是只读的。支持字段是!只有字段可以被标记为只读。 - Patrick Hofman

10

不同之处在于,生成的代码将在第二种情况下产生一个只读字段,并且该属性显然不会有setter。

让我们做一个真实的例子:

public class Test
{
    public Test(string name) { Name = name; }
    public string Name { get; private set; }
}
编译器会将它编译成这样:
public class Test
{
    private string <Name>k__BackingField;
    public Test(string name)
    {
        <Name>k__BackingField = name;
    }
    public string Name
    {
        get { return <Name>k__BackingField; }
        private set { <Name>k__BackingField = value; }
    }
}

正如您所看到的,编译器已经自动为您的属性创建了一个支持字段。该字段的名称将是那个神秘的名称,这是合法的.NET但不是C#,这意味着您永远不可能编写与此类自动生成的成员冲突的C#代码。

基本上,C#中的自动属性只是具有支持字段的属性的语法糖,实际编译的属性仍然具有支持字段,您只需不必显式编写它。

正如您所看到的,它还自动重写构造函数以直接写入该字段。请注意,在写入属性的任何地方,无论如何都不会有任何自定义代码存在。

现在让我们从属性中删除setter并看看会发生什么:

public class Test
{
    private readonly string <Name>k__BackingField;
    public Test(string name)
    {
        <Name>k__BackingField = name;
    }
    public string Name
    {
        get { return <Name>k__BackingField; }
    }
}

请注意,该字段现在是只读的,并且很明显,属性的setter方法也已经被移除。

因此,这实际上是创建具有真正只读属性的简单类型的最佳方式。不仅属性本身不可写入,而且后备字段也是只读的,这意味着您现在更适合轻松编写不可变类型。


有趣的是,我猜这也适用于静态只读属性(即仅声明自动获取器的情况)。 - Matías Fidemraizer
是的,在这方面 static 没有影响。 - Lasse V. Karlsen
我没有尝试查看这种情况的发生情况(使用仅具有“get;”的实例或私有属性)。因此,我不会手动声明更多的“readonly”字段;P(除非我需要不封装它们,但访问类字段而不封装仍然是一种不良编码实践)。 - Matías Fidemraizer

4

private的setter方法是一个只能从类内部调用的私有设置方法。

omitted的setter方法使属性变为只读。因此,您只能在构造函数或通过静态初始化来设置该属性的值。


3

如果一个属性省略了setter方法,在类构造函数内和类外其他地方都是只读的。

如果一个属性设置了私有的setter方法,那么在类外部(包括子类)是只读的,但在内部是可以被写入的。


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