如何拥有C#只读功能但不限于构造函数?

ath*_*hos 5 c# readonly lazy-initialization

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

现在假设我确实希望这个"赋值一次"约束,但我宁愿允许赋值在构造函数之外完成,懒惰/延迟评估/初始化也许.

我怎么能这样做?是否有可能以一种很好的方式做到这一点,例如,是否有可能写一些属性来描述这个?

Kev*_*sco 6

如果我正确理解你的问题,听起来你只想设置一次字段值(第一次),并且不允许在此之后设置它.如果是这样,那么关于使用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
Run Code Online (Sandbox Code Playgroud)


Mer*_*ham 3

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

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

如果你想在课堂上这样做

您可以使用 C# 4.0 内置的延迟初始化功能:

或者,对于旧版本的 C#,只需提供一个get方法,并检查是否已使用支持字段进行初始化:

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

        return someValue;
    }
}
Run Code Online (Sandbox Code Playgroud)

如果您想在课堂外进行此操作

你想要冰棒不变性:

基本上:

  • 您使整个可写,但添加一个Freeze方法。
  • 调用此 freeze 方法后,如果用户尝试在类上调用 setter 或 mutator 方法,则会抛出ModifyFrozenObjectException.
  • 您可能需要一种方法让外部类确定您的类是否IsFrozen.

顺便说一句,这些名字是我刚刚起的。诚然,我的选择很差,但目前还没有普遍遵循的惯例。

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

public interface IFreezable
{
    void Freeze();
    bool IsFrozen { get; }
}
Run Code Online (Sandbox Code Playgroud)