簡體   English   中英

如何擁有C#只讀功能但不限於構造函數?

[英]How to have a C# readonly feature but not limited to constructor?

C#“readonly”關鍵字是一個修飾符,當字段聲明包含它時,聲明引入的字段的賦值只能作為聲明的一部分或在同一個類的構造函數中出現。

現在假設我確實希望這個“賦值一次”約束,但我寧願允許賦值在構造函數之外完成,懶惰/延遲評估/初始化也許。

我怎么能這樣做? 是否有可能以一種很好的方式做到這一點,例如,是否有可能寫一些屬性來描述這個?

如果我正確理解你的問題,聽起來你只想設置一次字段值(第一次),並且不允許在此之后設置它。 如果是這樣,那么關於使用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內置的延遲初始化功能:

或者對於舊版本的C#,只需提供一個get方法,並檢查是否已使用支持字段初始化:

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

        return someValue;
    }
}

如果你想在課外做這件事

你想要冰棒不變性:

基本上:

  • 你使整個可寫,但添加一個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...
}

只有在第一次調用Foo屬性時才會調用GetFoo

這被稱為埃菲爾的“曾經”特征。 這是C#的主要疏忽。 新的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
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM