如何实现只读属性

发布于 2024-09-27 07:39:41 字数 528 浏览 5 评论 0原文

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

我看到有两种方法可以做到这一点:

  1. 类 MyClass
    {
        公共只读对象 MyProperty = new object();
    }
    
  2. 类 MyClass
    {
        私有只读对象 my_property = new object();
        公共对象 MyProperty { 获取 { 返回 my_property; } }
    }
    

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

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

I need to implement a read only property on my type. Moreover the value of this property is going to be set in the constructor and it is not going to be changed (I am writing a class that exposes custom routed UI commands for WPF but it does not matter).

I see two ways to do it:

  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; } }
    }
    

With all these FxCop errors saying that I should not have public member variables, it seems that the second one is the right way to do it. Is this correct?

Is there any difference between a get only property and a read only member in this case?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

小矜持 2024-10-04 07:39:41

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

public object MyProperty { get; }

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


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

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

序列化
从字段更改为属性可能会破坏很多序列化器。而且 AFAIK XmlSerializer 仅序列化公共属性,而不序列化公共字段。

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

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

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

C# 6.0 adds readonly auto properties

public object MyProperty { get; }

So when you don't need to support older compilers you can have a truly readonly property with code that's just as concise as a readonly field.


Versioning:
I think it doesn't make much difference if you are only interested in source compatibility.
Using a property is better for binary compatibility since you can replace it by a property which has a setter without breaking compiled code depending on your library.

Convention:
You are following the convention. In cases like this where the differences between the two possibilities are relatively minor following the convention is better. One case where it might come back to bite you is reflection based code. It might only accept properties and not fields, for example a property editor/viewer.

Serialization
Changing from field to property will probably break a lot of serializers. And AFAIK XmlSerializer does only serialize public properties and not public fields.

Using an Autoproperty
Another common Variation is using an autoproperty with a private setter. While this is short and a property it doesn't enforce the readonlyness. So I prefer the other ones.

Readonly field is selfdocumenting
There is one advantage of the field though:
It makes it clear at a glance at the public interface that it's actually immutable (barring reflection). Whereas in case of a property you can only see that you cannot change it, so you'd have to refer to the documentation or implementation.

But to be honest I use the first one quite often in application code since I'm lazy. In libraries I'm typically more thorough and follow the convention.

近箐 2024-10-04 07:39:41

第二种方式是首选。

private readonly int MyVal = 5;

public int MyProp { get { return MyVal;}  }

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

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

The second way is the preferred option.

private readonly int MyVal = 5;

public int MyProp { get { return MyVal;}  }

This will ensure that MyVal can only be assigned at initialization (it can also be set in a constructor).

As you had noted - this way you are not exposing an internal member, allowing you to change the internal implementation in the future.

您的好友蓝忘机已上羡 2024-10-04 07:39:41

随着 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 是没有区别的有一个私人二传手。

With the introduction of C# 6 (in VS 2015), you can now have get-only automatic properties, in which the implicit backing field is readonly (i.e. values can be assigned in the constructor but not elsewhere):

public string Name { get; }

public Customer(string name)  // Constructor
{
    Name = name;
}

private void SomeFunction()
{
    Name = "Something Else";  // Compile-time error
}

And you can now also initialise properties (with or without a setter) inline:

public string Name { get; } = "Boris";

Referring back to the question, this gives you the advantages of option 2 (public member is a property, not a field) with the brevity of option 1.

Unfortunately, it doesn't provide a guarantee of immutability at the level of the public interface (as in @CodesInChaos's point about self-documentation), because to a consumer of the class, having no setter is indistinguishable from having a private setter.

空名 2024-10-04 07:39:41

在 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

In C# 9, Microsoft introduced a new way to have properties set only on initialization using the init accessor, like so:

public class Person
{
  public string FirstName { get; init; }
  public string LastName { get; init; }
}

This way, you can assign values when initializing a new object:

var person = new Person
{
  Firstname = "John",
  LastName = "Doe"
}

But later on, you cannot change it:

person.LastName = "Denver"; // throws a compiler error
旧夏天 2024-10-04 07:39:41

你可以这样做:

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

You can do this:

public int Property { get { ... } private set { ... } }
葬花如无物 2024-10-04 07:39:41

还有另一种方式(我最喜欢),从 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

yet another way (my favorite), starting with 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

眼眸里的快感 2024-10-04 07:39:41

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

I agree that the second way is preferable. The only real reason for that preference is the general preference that .NET classes not have public fields. However, if that field is readonly, I can't see how there would be any real objections other than a lack of consistency with other properties. The real difference between a readonly field and get-only property is that the readonly field provides a guarantee that its value will not change over the life of the object and a get-only property does not.

日裸衫吸 2024-10-04 07:39:41

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

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

The second method is preferred because of the encapsulation. You can certainly have the readonly field be public, but that goes against C# idioms in which you have data access occur through properties and not fields.

The reasoning behind this is that the property defines a public interface and if the backing implementation to that property changes, you don't end up breaking the rest of the code because the implementation is hidden behind an interface.

葮薆情 2024-10-04 07:39:41

试试这个:

public string FirstName { get; set; }
public string LastName { get; set; }
public string? Name
    {
        get
        {
            return string.Format("{0} {1}", FirstName, LastName);
        }
    }

Try this:

public string FirstName { get; set; }
public string LastName { get; set; }
public string? Name
    {
        get
        {
            return string.Format("{0} {1}", FirstName, LastName);
        }
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文