5

C#の「readonly」キーワードは、フィールド宣言に含まれている場合、宣言によって導入されたフィールドへの割り当ては、宣言の一部として、または同じクラスのコンストラクターでのみ発生する修飾子です。

ここで、この「値を1回割り当てる」制約が必要であると仮定しますが、コンストラクターの外部で割り当てを実行できるようにします。おそらく、遅延/遅延評価/初期化です。

どうすればそれができますか?そして、それを良い方法で行うことは可能ですか?たとえば、これを説明するための属性を書くことは可能ですか?

4

4 に答える 4

6

私があなたの質問を正しく理解しているなら、あなたはただ一度(最初に)フィールドの値を設定したいと思っているように思えます、そしてそれ以降はそれを設定することを許可しません。もしそうなら、レイジー(および関連する)の​​使用に関する以前のすべての投稿が役立つかもしれません。しかし、これらの提案を使用したくない場合は、おそらく次のようなことを行うことができます。

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
于 2011-11-11T01:47:58.647 に答える
3

ここで、この「値を1回割り当てる」制約が必要であると仮定しますが、コンストラクターの外部で割り当てを実行できるようにします。

遅延初期化は複雑であることに注意してください。したがって、これらすべての回答について、オブジェクトにアクセスしようとしている複数のスレッドがある場合は注意が必要です。

クラス内でこれを行いたい場合

C#4.0の組み込みの遅延初期化機能を使用できます。

または、古いバージョンのC#の場合は、getメソッドを指定し、バッキングフィールドを使用して既に初期化されているかどうかを確認します。

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

        return someValue;
    }
}

クラスの外でこれを行いたい場合

アイスキャンデーの不変性が必要です。

基本的に:

  • クラス全体を書き込み可能にしますが、メソッドを追加しFreezeます。
  • このフリーズメソッドが呼び出された後、ユーザーがクラスでセッターまたはミューテーターメソッドを呼び出そうとすると、。がスローされますModifyFrozenObjectException
  • おそらく、外部クラスが自分のクラスかどうかを判断する方法が必要ですIsFrozen

ところで、私はちょうど今これらの名前を作りました。私の選択は確かに貧弱ですが、これについて一般的に守られている慣習はまだありません。

今のところ、IFreezableインターフェイスと、場合によっては関連する例外を作成することをお勧めします。これにより、WPFの実装に依存する必要がなくなります。何かのようなもの:

public interface IFreezable
{
    void Freeze();
    bool IsFrozen { get; }
}
于 2011-11-11T01:07:45.483 に答える
1

これは、Eiffelの「1回限り」の機能として知られています。これはC#の主要な見落としです。新しいレイジータイプは、レイジーでないバージョンと互換性がないため、代替としては不十分ですが、代わりに、Valueプロパティを介して含まれている値にアクセスする必要があります。したがって、私はめったに使用しません。ノイズは、C#コードの最大の問題の1つです。理想的には、このようなものが必要です...

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
    }
}
于 2012-02-24T16:07:46.540 に答える
1

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

GetFooFooプロパティを初めて呼び出すときにのみ呼び出されます。

于 2011-11-11T01:14:03.013 に答える