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

C# 只读修饰符和私有setter之间哪个更好?,c#,readonly,private-members,C#,Readonly,Private Members,我一直在努力创建一个类,突然想到这两个代码之间的区别: public readonly string ProductLocation; 及 你们能告诉我什么时候更好地使用下面的方法吗。谢谢。第一个字段是只读字段,而第二个字段被编译为一对方法(对属性ProductLocation的所有读取被编译成对相应get方法的调用,对它的写入被编译成对set方法的调用;在内部,这些方法将从内部自动生成的非只读字段读取/写入).我想说,最重要的区别是线程安全性。(如何?请继续阅读!) 该类的基本用法看起来完全

我一直在努力创建一个类,突然想到这两个代码之间的区别:

public readonly string ProductLocation;


你们能告诉我什么时候更好地使用下面的方法吗。谢谢。

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

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

第一个最细微的区别是,具有私有setter的属性允许同一类的实例修改值,而对于只读属性,即使对象本身也无法更改值

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

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

为了实现与属性相同的线程安全性,您必须在代码上添加一些容易出错的同步。这可能会导致死锁、数据争用或性能降低,具体取决于具体情况,尤其是在您没有经验的情况下

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

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

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


我会将前者用于您知道不会更改的内容,将后者用于值可能更改但您不希望其他人更改的内容。

一般来说,.NET不鼓励公开成员字段,这些字段应该由属性包装。因此,假设您可能

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

在这种设置中,忽略通过反射可以实现的功能,语义是在第一种情况下,
productLocation
变量只能在类构造函数中就地初始化。类的其他成员不能更改值。外部使用者无法设置值

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


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

第一个是字段,其值只能在实例化时设置

第二个是属性,其值可以在任何时间设置(但只能由其包含的对象设置)


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

使用自动属性初始值设定项没有那么简单的方法

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

public string ProductLocation { get; } 
这是只读的。仅从构造函数或内联初始化。初始化后无法编辑。(从任何位置都不可变)

但是,如果使用私有集

public string ProductLocation { get; private set } 

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

请注意,
private set;
选项不是线程安全的。建议使用包装只读字段,真的吗?我是一个懒惰的开发人员,这就是我提出这个问题的原因。public readonly string ProductLocation绝对是较短的版本,因此对我来说似乎更好。这是不正确的。属性可以是在任何时候由同一类的任何实例设置(不仅仅是由其包含对象设置)。@BrunoReis,那么实例和包含对象之间的区别是什么?因为没有实例,就没有对象。有人能澄清一下吗?!对我来说,最重要的区别是immuta
public string ProductLocation { get; } 
public string ProductLocation { get; private set }