如何拥有c#只读特性,但不局限于构造函数

本文关键字:构造函数 局限于 只读 何拥有 拥有 | 更新日期: 2023-09-27 18:14:15

c# "readonly"关键字是一个修饰符,当字段声明中包含它时,对声明引入的字段的赋值只能作为声明的一部分或在同一类的构造函数中发生。

现在假设我确实想要这个"赋值一次"的约束,但我宁愿允许赋值在构造函数之外完成,可能是一个延迟的求值/初始化。

我怎么能那样做呢?有没有可能用一种更好的方式来表达,比如,有没有可能用一些属性来描述它?

如何拥有c#只读特性,但不局限于构造函数

如果我正确理解你的问题,听起来你只是想设置一个字段的值一次(第一次),而不允许它被设置之后。如果是这样的话,那么之前关于使用Lazy(和相关的)的所有帖子可能都是有用的。但是如果你不想使用这些建议,也许你可以这样做:

public class SetOnce<T> 
{
    private T mySetOnceField;
    private bool isSet;
    // used to determine if the value for 
    // this SetOnce object has already been set.
    public bool IsSet
    {
      get { return isSet; }
    }
    // return true if this is the initial set, 
    // return false if this is after the initial set.
    // alternatively, you could make it be a void method
    // which would throw an exception upon any invocation after the first.
    public bool SetValue(T value)
    {
       // or you can make thread-safe with a lock..
       if (IsSet)
       {
          return false; // or throw exception.
       }
       else 
       {
          mySetOnceField = value;
          return isSet = true;
       }
    }
    public T GetValue()
    {
      // returns default value of T if not set. 
      // Or, check if not IsSet, throw exception.
      return mySetOnceField;         
    }
} // end SetOnce
public class MyClass 
{
  private SetOnce<int> myReadonlyField = new SetOnce<int>();
  public void DoSomething(int number)
  {
     // say this is where u want to FIRST set ur 'field'...
     // u could check if it's been set before by it's return value (or catching the exception).
     if (myReadOnlyField.SetValue(number))
     {
         // we just now initialized it for the first time...
         // u could use the value: int myNumber = myReadOnlyField.GetValue();
     }
     else
     {
       // field has already been set before...
     }
  } // end DoSomething
} // end MyClass

现在假设我确实想要这个"赋值一次"的约束,但我宁愿允许赋值在构造函数之外完成

请注意,延迟初始化是复杂的,所以对于所有这些答案,您应该小心,如果您有多个线程试图访问您的对象。

如果你想在类

你可以使用c# 4.0内置的延迟初始化特性:

  • http://msdn.microsoft.com/en-us/library/dd997286.aspx
  • http://msdn.microsoft.com/en-us/library/dd642331.aspx
  • http://sankarsan.wordpress.com/2009/10/04/laziness-in-c-4-0-lazyt/

或者对于旧版本的c#,只需提供一个get方法,并检查是否已经通过使用backing字段初始化:

public string SomeValue
{
    get
    {
        // Note: Not thread safe...
        if(someValue == null)
        {
            someValue = InitializeSomeValue(); // Todo: Implement
        }
        return someValue;
    }
}

如果你想在类之外做这个

你想要冰棒不变性:

  • http://blogs.msdn.com/b/ericlippert/archive/2007/11/13/immutability-in-c-part-one-kinds-of-immutability.aspx
  • http://msdn.microsoft.com/en-us/library/ms750509.aspx
  • http://csharpindepth.com/Talks.aspx(搜索"冰棒不变性",你会发现一个视频)
基本上

:

  • 你让整个是可写的,但是增加了一个Freeze方法。
  • 一旦这个冻结方法被调用,如果用户试图在你的类上调用setter或mutator方法,你抛出一个ModifyFrozenObjectException
  • 你可能想要一个外部类来确定你的类IsFrozen .
顺便说一句,这些名字是我刚刚编的。无可否认,我的选择很糟糕,但目前还没有普遍遵循的惯例。

现在我建议您创建一个IFreezable接口,并可能创建相关的异常,这样您就不必依赖于WPF实现。比如:

public interface IFreezable
{
    void Freeze();
    bool IsFrozen { get; }
}

您可以使用Lazy<T>类:

private readonly Lazy<Foo> _foo = new Lazy<Foo>(GetFoo);
public Foo Foo
{
    get { return _foo.Value; }
}
private static Foo GetFoo()
{
    // somehow create a Foo...
}

GetFoo只会在第一次调用Foo属性时被调用。

这就是埃菲尔铁塔的"曾经"特征。这是c#中的一个主要疏忽。新的Lazy类型是一个糟糕的替代品,因为它不能与非Lazy版本互换,而是要求您通过其value属性访问包含的值。因此,我很少使用它。噪声是c#代码最大的问题之一。理想情况下,人们想要这样的东西……

public once Type PropertyName { get { /* generate and return value */ } }

与当前的最佳实践相反…

Type _PropertyName; //where type is a class or nullable structure
public Type PropertyName
{
    get
    {
        if (_PropertyName == null)
            _PropertyName = /* generate and return value */ 
        return _PropertyName
    }
}