如何实现只读属性

时间:2010-10-12 18:29:03

标签: c# properties readonly

我需要在我的类型上实现只读属性。此外,此属性的值将在构造函数中设置,并且不会更改(我正在编写一个为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; } }
    }
    
  3. 由于所有这些FxCop错误都说我不应该有公共成员变量,所以第二个似乎是正确的方法。正确的吗?

    在这种情况下,get only属性和只读成员之间是否有任何区别?

    我将不胜感激任何意见/建议/等。

8 个答案:

答案 0 :(得分:57)

第二种方式是首选方案。

private readonly int MyVal = 5;

public int MyProp { get { return MyVal;}  }

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

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

答案 1 :(得分:46)

<强>版本:
如果您只对源代码兼容性感兴趣,我认为这没什么区别 使用属性更适合二进制兼容性,因为您可以使用具有setter的属性替换它,而不会破坏编译代码,具体取决于您的库。

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

<强>序列化
从字段更改为属性可能会破坏许多序列化程序。 AFAIK XmlSerializer仅对公共属性进行序列化,而不是公共字段。

使用Autoproperty
另一个常见的Variation是使用带有私有设置器的autoproperty。虽然这很短并且属性不会强制执行readonlyness。所以我更喜欢其他的。

只读字段是自我记录
该领域的一个优点是:
它使一目了然的公共界面清楚地表明它实际上是不可变的(禁止反思)。而在属性的情况下,您只能看到无法更改它,因此您必须参考文档或实现。

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

C#6.0添加了只读自动属性

public object MyProperty { get; }

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

答案 2 :(得分:40)

随着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无法区分设定器。

答案 3 :(得分:11)

你可以这样做:

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

答案 4 :(得分:5)

我同意第二种方式更可取。这种偏好的唯一真正原因是一般偏好.NET类没有公共字段。但是,如果该字段是只读的,我看不出除了与其他属性缺乏一致性之外会有什么真正的异议。 readonly字段和get-only属性之间的真正区别在于readonly字段提供了一个保证,它的值在对象的生命周期内不会改变,而get-only属性则不会。

答案 5 :(得分:4)

由于封装,第二种方法是优选的。你当然可以将readonly字段公之于众,但这违反了C#习语,你可以通过属性而不是字段来访问数据。

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

答案 6 :(得分:1)

在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

答案 7 :(得分:0)

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

private readonly int MyVal = 5;

public int MyProp => MyVal;

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

相关问题