【问题标题】:Is there a way of setting a property only once as a built-in mechanism?有没有一种方法可以将属性设置为仅一次作为内置机制?
【发布时间】:2017-01-27 21:25:23
【问题描述】:

我在 2009 年 5 月 8 日 13:29 看到了 Marc Gravell 的回答 :

public sealed class WriteOnce<T>
{
    private T value;
    private bool hasValue;
    public override string ToString()
    {
        return hasValue ? Convert.ToString(value) : "";
    }
    public T Value
    {
        get
        {
            if (!hasValue) throw new InvalidOperationException("Value not set");
            return value;
        }
        set
        {
            if (hasValue) throw new InvalidOperationException("Value already set");
            this.value = value;
            this.hasValue = true;
        }
    }
    public T ValueOrDefault { get { return value; } }

    public static implicit operator T(WriteOnce<T> value) { return value.Value; }
}
Then use, for example:

readonly WriteOnce<string> name = new WriteOnce<string>();
public WriteOnce<string> Name { get { return name; } }

但是我不明白为什么要创建readonly WriteOnce&lt;T&gt;,如果它的值是private,并且它使用的属性Value只能设置一次。 我也不明白为什么要创建一个属性Name,它只启用get而不启用set:

1.你不能设置名称的值,因为它是只读的并且

2.你不能通过属性设置它的值,因为它只能获取。

【问题讨论】:

    标签: c# templates properties get set


    【解决方案1】:

    你在这里混淆了很多东西。

    1. 只读字段意味着它只能在构造函数内或通过字段初始化器赋值。现在WriteOnce是引用类型,所以赋值只表示name中存储的值是对新创建的WriteOnce&lt;string&gt;对象的引用。

      没有什么可以阻止您随时使用name.Value = "Hello";,因为您没有更改name 的值。另一方面,在构造函数或字段初始值设定项之外的name = whatever 是不允许的,因为您将变量的值更改为新的引用,但仅此而已。

    2. Name 是一个只读属性,它的支持字段为name

      只读属性不允许您执行Name = new WriteOnce&lt;string&gt;(),但Name.Value = "Hello" 完全可以。

      无论如何,现在,您只需使用只读的自动属性并让编译器生成所有管道代码(支持字段):

      public WriteOnce<string> Name { get }
      

    【讨论】:

    • public WriteOnce Name { get } 不会让您以“可读”方式创建实例var instance = new WriteOnce { Name = "test"};
    • @Fabio 这个代码甚至没有使用自动属性我很确定这已经很老了,代码的意图是 name 是通过构造函数参数设置的。
    • 非常感谢,@Tom 的解释让我非常清楚
    【解决方案2】:

    readonly 表示对象只能在构造函数中创建或更改。将其设为私有而不是 readonly 将允许任何方法创建新的 name 字段。因此,您确实可以只设置一次 Value,但如果对象不是只读的,您可以将其完全替换为新对象。

    Creatint readonly WriteOnce 表示您可以随时设置name 的值,而不仅仅是在构造函数中,但是一旦设置了值就无法更改它,也无法将其替换为新的WriteOnce对象。

    【讨论】:

      【解决方案3】:

      您可以将类重写为结构,使其更简单易读。

      struct WriteOnce<T>
      {
          public T Value { get; }
      
          public WriteOnce(T input)
          {
              Value = input;
          }
      
          public static implicit operator WriteOnce<T>(T input)
          {
              return new WriteOnce<T>(input);
          }
      }
      

      用法:

      WriteOnce<string> name = "test";
      

      您只能在实例化时更改该值,以便您始终知道它将是什么。

      【讨论】:

      • 愿意解释否决票,以便我能更好地澄清吗?它回答了他的头衔。
      • WriteOnce 设为结构不会让您执行以下操作:SomeClass.Name.Value = "Hello";。您将收到一个编译时错误,因为该代码不会按照您的预期执行(这基本上没有什么用处):Can not modify the return value of SomeClass.Name because it is not a variable. 这就是为什么要避免可变结构的原因之一,只是建议使用一个是已经值得投反对票了。
      • 是的......你不应该能够。这就是代码的全部目的。为什么你需要能够做到这一点?而且这个结构是不可变的......什么?......
      • 这与WriteOnce 无关,它与SomeClass.Name 将为您提供对象的副本 的事实有关,因为这就是值类型的工作方式。您将修改将立即丢​​弃的副本的属性,并且您认为正在修改的对象将保持不变。这将是该语言的一个不幸的gotcha,因此编译器不允许它。如果Name 是引用类型,那么您将获得 reference 的副本并且一切正常,因为引用的副本仍将指向同一个对象。
      • 使用不可变值类型可以完全避免该问题,因为您无法更改对象,因此您一开始就不会落入这个陷阱。现在有意义吗?
      猜你喜欢
      • 2021-08-14
      • 2012-02-25
      • 1970-01-01
      • 1970-01-01
      • 2013-05-15
      • 1970-01-01
      • 2021-04-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多