我需要在我的类型上实现一个 read only 属性。此外,此属性的值将在构造函数中设置,并且不会更改(我正在编写一个为 WPF 公开自定义路由 UI 命令的类,但这并不重要)。
我看到两种方法:
class MyClass
{
public readonly object MyProperty = new object();
}
class MyClass
{
private readonly object my_property = new object();
public object MyProperty { get { return my_property; } }
}
所有这些 FxCop 错误都表明我不应该有公共成员变量,看来第二个是正确的方法。这是正确的吗?
在这种情况下,仅获取属性和只读成员之间有什么区别吗?
C# 6.0 添加只读自动属性
public object MyProperty { get; }
因此,当您不需要支持较旧的编译器时,您可以拥有真正的只读属性,其代码与只读字段一样简洁。
版本控制:
我认为如果您只对源兼容性感兴趣,那没有多大区别。
使用属性对于二进制兼容性更好,因为您可以将其替换为具有设置器的属性,而不会破坏编译的代码,具体取决于您的库。
惯例:
你遵守惯例。在这种情况下,两种可能性之间的差异相对较小,遵循惯例更好。一种可能会给你带来麻烦的情况是基于反射的代码。它可能只接受属性而不接受字段,例如属性编辑器/查看器。
连载
从字段更改为属性可能会破坏很多序列化器。而且 AFAIK
XmlSerializer
只序列化公共属性而不是公共字段。
使用自动属性
另一种常见的变体是使用带有私有设置器的自动属性。虽然这很短并且是一个属性,但它并不强制只读性。所以我更喜欢其他的。
只读字段是自记录的
不过,该领域有一个优势:
公共接口一目了然,它实际上是不可变的(除非反射)。而对于属性,您只能看到you无法更改它,因此您必须参考文档或实现。
但说实话,我在应用程序代码中经常使用第一个,因为我很懒。在图书馆,我通常会更彻底并遵循惯例。
第二种方式是首选。
private readonly int MyVal = 5;
public int MyProp { get { return MyVal;} }
这将确保
MyVal
只能在初始化时分配(也可以在构造函数中设置)。
正如您所指出的 - 这样您就不会暴露内部成员,从而允许您将来更改内部实现。
随着 C# 6(在 VS 2015 中)的引入,您现在可以拥有仅限
get
的自动属性,其中隐式支持字段为 readonly
(即可以在构造函数中分配值,但不能在其他地方分配值):
public string Name { get; }
public Customer(string name) // Constructor
{
Name = name;
}
private void SomeFunction()
{
Name = "Something Else"; // Compile-time error
}
您现在还可以内联初始化属性(带或不带 setter):
public string Name { get; } = "Boris";
回到问题,这为您提供了选项 2 的优点(公共成员是属性,而不是字段),并且选项 1 简洁。
不幸的是,它没有提供公共接口级别的不变性保证(如 @CodesInChaos 关于自我文档的观点),因为对于该类的使用者来说,没有 setter 与拥有私有 setter 没有什么区别。
在 C# 9 中,Microsoft 引入了一种仅在初始化时使用
init
访问器 设置属性的新方法,如下所示:
public class Person
{
public string FirstName { get; init; }
public string LastName { get; init; }
}
这样就可以在初始化新对象时进行赋值了:
var person = new Person
{
Firstname = "John",
LastName = "Doe"
}
但是以后你就无法更改它了:
person.LastName = "Denver"; // throws a compiler error
你可以这样做:
public int Property { get { ... } private set { ... } }
还有另一种方式(我最喜欢),从 C# 6 开始
private readonly int MyVal = 5;
public int MyProp => MyVal;
我同意第二种方式更好。 这种偏好的唯一真正原因是人们普遍偏好 .NET 类没有公共字段。 但是,如果该字段是只读的,除了与其他属性缺乏一致性之外,我看不出还会有任何真正的反对意见。 只读字段和仅获取属性之间的真正区别在于,只读字段保证其值在对象的生命周期内不会改变,而仅获取属性则不会。
由于封装,首选第二种方法。 您当然可以将只读字段设置为公共,但这违背了 C# 习惯用法,在 C# 习惯用法中,数据访问是通过属性而不是字段进行的。
这背后的原因是该属性定义了一个公共接口,如果该属性的支持实现发生变化,您最终不会破坏其余的代码,因为该实现隐藏在接口后面。
试试这个:
public string FirstName { get; set; }
public string LastName { get; set; }
public string? Name
{
get
{
return string.Format("{0} {1}", FirstName, LastName);
}
}