只读修饰符和私有 setter 之间哪个更好?

我一直致力于创建一个类,突然间我想到了这两个代码的区别:

public readonly string ProductLocation;

还有

public string ProductLocation
{
get;
private set;
}

你们能告诉我什么时候更好地使用以下内容吗? 谢谢。

23577 次浏览

第一个是只读字段,而第二个被编译为一对方法(所有对属性 ProductLocation的读取都被编译成对相应 get方法的调用,对它的写入被编译成对 set方法的调用; 在内部,这些方法将从/写入到一个内部的自动生成的非只读字段)。我认为最重要的区别是线程安全性!(怎么做到的?继续读!)

这个类的基本用法看起来和 没错一样: 其他类中的代码只能读取值,而不能更改它。另外,读取值的代码看起来也完全一样(例如,print(myInstace.ProductLocation); 在这里,您无法说明它是如何声明的,很酷吧?)

首先,最微不足道的区别是,带有私有 setter 的属性允许同一类的实例修改值,而在只读属性的情况下,甚至对象本身也不能修改值。

现在,为了线程安全。当您使用多个线程时(就像 Java 的 final字段一样) ,字段上的 readonly属性将改变其内存可见性语义。

readonly字段只能在声明时或在构造函数中赋值。分配给 readonly字段的值不能更改(至少不能以 < em > Normal 的方式更改) ,并且保证每个线程在构造函数返回后都能看到正确的初始化值.因此,readonly字段本质上是线程安全的。

要使用该属性实现相同的线程安全性,必须在代码上添加一些同步,这很容易出错。它可能导致死锁、数据竞争或性能降低,这取决于具体情况,特别是如果您没有经验。

因此,如果该值表示在对象构造之后 语义上的不能更改的内容,则不应声明私有 setter (这意味着对象可能会更改它)。进入 readonly 字段(可以将其声明为 private 并声明一个只有 getter 访问该字段的 public 属性!这实际上是首选的形式,因为公开字段并不好,所以最好只公开方法——在 这个答案中有很多原因可以解释这一点

第一个(使用 readonly)将意味着对象 甚至不能修改自己字段的值,一旦对象被实例化,其他对象永远不能修改它。

第二个(使用 private set)意味着对象 可以在实例化后修改其字段的值,但其他对象永远不能修改它。

我会将前者用于您所知道的 不会改变,而将后者用于值可能会改变但您不希望其他人改变它的情况。

一般来说,不鼓励。NET 来公开公开成员字段,则这些字段应由属性包装。所以我们假设你可能有

private readonly string productLocation;
public string ProductLocation { get { return productLocation; } }

public string ProductLocation { get; private set; }

在这种设置中,忽略通过反射可以完成的任务,语义是在第一种情况下,productLocation变量只能在适当的地方和类构造函数中初始化。类的其他成员不能更改该值。外部使用者没有设置价值的能力。

在第二个版本中,外部使用者仍然没有设置值的权限。但是,类本身可以随时更改该值。如果您只有一个 DTO (也就是说,一个只传输数据的类,它没有通过方法表达的逻辑) ,那么这与 readonly版本本质上没有什么不同。但是,对于带有方法的类,这些方法可能会改变 ProductLocation后面的值。

如果希望在构造后强制执行不可变字段的概念,请使用 readonly。但是对于 DTO,我可能会选择 private set;选项,主要是因为它的样板代码较少。

第一个是 场地,其值可以设置为 只有在实例化的时候

第二个是 财产,其值可以设置为 任何时候(但只能通过其包含的对象)。


更正: 该属性可以在任何时候由同一类的任何实例设置(而不仅仅是由其包含的对象设置)。

对于 C # 6.0 自动属性初始化程序,有更少的样板操作方式

private readonly string productLocation;
public string ProductLocation { get { return productLocation; } }

也就是

public string ProductLocation { get; }

这是只读的。只从构造函数或内联初始化。初始化后无法编辑。(任何地方不可变)

但是,如果使用私有集合;

public string ProductLocation { get; private set }

这是从外部只读的。但是可以在类本身的任何时间任何地点进行初始化。并且可以在其生命周期内由类本身进行编辑。(阶级可变,外界不可变)