如何实现只读属性

问题描述 投票:0回答:9

我需要在我的类型上实现一个 read only 属性。此外,此属性的值将在构造函数中设置,并且不会更改(我正在编写一个为 WPF 公开自定义路由 UI 命令的类,但这并不重要)。

我看到两种方法:

  1. class MyClass
    {
        public readonly object MyProperty = new object();
    }
    
  2. class MyClass
    {
        private readonly object my_property = new object();
        public object MyProperty { get { return my_property; } }
    }
    

所有这些 FxCop 错误都表明我不应该有公共成员变量,看来第二个是正确的方法。这是正确的吗?

在这种情况下,仅获取属性和只读成员之间有什么区别吗?

c# properties readonly
9个回答
85
投票

C# 6.0 添加只读自动属性

public object MyProperty { get; }

因此,当您不需要支持较旧的编译器时,您可以拥有真正的只读属性,其代码与只读字段一样简洁。


版本控制:
我认为如果您只对源兼容性感兴趣,那没有多大区别。
使用属性对于二进制兼容性更好,因为您可以将其替换为具有设置器的属性,而不会破坏编译的代码,具体取决于您的库。

惯例:
你遵守惯例。在这种情况下,两种可能性之间的差异相对较小,遵循惯例更好。一种可能会给你带来麻烦的情况是基于反射的代码。它可能只接受属性而不接受字段,例如属性编辑器/查看器。

连载
从字段更改为属性可能会破坏很多序列化器。而且 AFAIK

XmlSerializer
只序列化公共属性而不是公共字段。

使用自动属性
另一种常见的变体是使用带有私有设置器的自动属性。虽然这很短并且是一个属性,但它并不强制只读性。所以我更喜欢其他的。

只读字段是自记录的
不过,该领域有一个优势:
公共接口一目了然,它实际上是不可变的(除非反射)。而对于属性,您只能看到you无法更改它,因此您必须参考文档或实现。

但说实话,我在应用程序代码中经常使用第一个,因为我很懒。在图书馆,我通常会更彻底并遵循惯例。


83
投票

第二种方式是首选。

private readonly int MyVal = 5;

public int MyProp { get { return MyVal;}  }

这将确保

MyVal
只能在初始化时分配(也可以在构造函数中设置)。

正如您所指出的 - 这样您就不会暴露内部成员,从而允许您将来更改内部实现。


61
投票

随着 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 没有什么区别。


28
投票

在 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

13
投票

你可以这样做:

public int Property { get { ... } private set { ... } }

7
投票

还有另一种方式(我最喜欢),从 C# 6 开始

private readonly int MyVal = 5;

public int MyProp => MyVal;

https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/properties#expression-body-definitions


5
投票

我同意第二种方式更好。 这种偏好的唯一真正原因是人们普遍偏好 .NET 类没有公共字段。 但是,如果该字段是只读的,除了与其他属性缺乏一致性之外,我看不出还会有任何真正的反对意见。 只读字段和仅获取属性之间的真正区别在于,只读字段保证其值在对象的生命周期内不会改变,而仅获取属性则不会。


4
投票

由于封装,首选第二种方法。 您当然可以将只读字段设置为公共,但这违背了 C# 习惯用法,在 C# 习惯用法中,数据访问是通过属性而不是字段进行的。

这背后的原因是该属性定义了一个公共接口,如果该属性的支持实现发生变化,您最终不会破坏其余的代码,因为该实现隐藏在接口后面。


0
投票

试试这个:

public string FirstName { get; set; }
public string LastName { get; set; }
public string? Name
    {
        get
        {
            return string.Format("{0} {1}", FirstName, LastName);
        }
    }
© www.soinside.com 2019 - 2024. All rights reserved.