4

(Bar) という名前のプロパティを遅延ロードするクラス (Foo) があります。初期化されていないバッキング フィールドを (インテリセンスまたは経験の浅いスタッフによる) 誤った使用から保護するための優先する方法は何ですか?

私は3つのオプションを考えることができます:

  class Foo {
    // option 1 - Easy to use this.bar by mistake. 
    string bar;
    string Bar {
        get {
            // logic to lazy load bar  
            return bar; 
        }
    }

    // option 2 - Harder to use this._bar by mistake. It is more obscure.
    string _bar2;
    string Bar2 {
        get {
            // logic to lazy load bar2  
            return _bar2;
        }
    }

    //option 3 - Very hard to use the backing field by mistake. 
    class BackingFields {
        public string bar; 
    }

    BackingFields fields = new BackingFields();

    string Bar3 {
        get {
            // logic to lazy load bar  
            return fields.bar;
        }
    }

}

バッキング フィールド バーをいじくり回してほしい唯一の場所は、プロパティのセッターとゲッターであることを覚えておいてください。クラスの他の場所では、常に this.Bar を使用する必要があります

アップデート

私は現在、次の Lazy 実装を使用しています(バッキング フィールドを持つすべてのプロパティではなく、遅延読み込み、同期、および通知を必要とする一部のプロパティに対して)。先物もサポートするように拡張できます(後で別のスレッドで評価を強制します)

私の実装は、外部セットをサポートしているため、読み取り時にロックされます。

また、これはRuby などで克服できる言語の制限だと思います。

この方法で遅延を実装できます。

x = lazy do
    puts "<<< Evaluating lazy value >>>"
    "lazy value"
end

puts x
# <<< Evaluating lazy value >>>
# lazy value
4

14 に答える 14

5

それでは見逃せないObsoleteAttributeアンド~を使ってみてはいかがでしょうか!#pragma

    void Test1()
    {
        _prop = ""; // warning given
    }
    public string Prop
    {
#pragma warning disable 0618
        get { return _prop; }
        set { _prop = value; }
#pragma warning restore 0618
    }
    [Obsolete("This is the backing field for lazy data; do not use!!")]
    private string _prop;
    void Test2()
    {
        _prop = ""; // warning given
    }
于 2009-02-18T10:59:45.103 に答える
4

オプション 5

Lazy<T>

いくつかの状況で非常にうまく機能しますが、開発者が馬鹿でない限り、ほとんどのプロジェクトではオプション 1 で問題ありません。

[EditorBrowsable(EditorBrowsableState.Never)] をフィールドに追加しても、プライベートの場合は役に立ちません。このロジックは、現在のコードではなくメタデータから生成されたインテリセンスに対してのみ有効になるためです (現在のプロジェクトと、dll ではなくプロジェクト参照を介して行われたもの)。

注:Lazy<T>スレッドセーフではありません(これは良いことです。必要がない場合はポイントロックはありません)スレッドセーフが必要な場合は、Joe DuffyまたはParallel Exetensions CTPのスレッドセーフなもののいずれかを使用してください

于 2009-02-18T10:58:41.907 に答える
3

オプション 1 はコード レビューに合格しますが、後で間違いを見つけやすいため、通常はオプション 2 を使用します。オプション 3 は複雑に見えますが、機能する可能性はありますが、リファクタリングやバグの修正などを試みながら 6 か月後に再検討するのは適切なコードではありません。

于 2009-02-18T10:48:30.497 に答える
2

オプション 1、ある程度の教育を伴う。

理論的根拠: ソフトウェアは、書かれるよりも頻繁に読み取られるように意図されているため、一般的なケースに合わせて最適化し、読みやすい状態に保ちます。

于 2009-02-18T10:43:18.120 に答える
2

コードレビューは誤用を見つけるので、最も読みやすいものを使用してください. 1) うまくいかない、2) 賢いプログラマーが仕事をやり遂げるのを難しくする、3) 問題の原因ではなく症状に対処するため、私はコードで悪いプログラマーを回避しようとする試みを嫌います。

于 2009-02-18T10:59:41.560 に答える
1

クラスの一番上に、次のような大きなコメントブロックを配置します。

/************************************************************
* Note: When updating this class, please take care of using *
*       only the accessors to access member data because of *
*       ... (state the reasons / your name, so they can ask *
*       questions)                                          *
*************************************************************/

通常は、そのようなメモで十分ですが、これがプロジェクト内のすべてのクラスで同じである場合は、プロジェクトに取り組んでいるプログラマーに提供する簡単なドキュメントに入れて、コードが表示されるたびに配置することをお勧めします。それが適合していない場合は、ドキュメントをポイントします。

于 2009-02-27T21:01:13.913 に答える
1

私は通常、オプション 1 を選択します。これはプライベート フィールドであるため、実際には問題になるとは思いません。オプション 3 のようにラッパー クラスのようなものを使用すると、コードが読みにくく、理解しにくくなります。

于 2009-02-18T10:42:21.493 に答える
0

自動プロパティ:

public int PropertyName { get; set; }

バッキングフィールドへのアクセスを防ぎます。しかし、そこにコードを入れたい場合 (たとえば、最初のアクセスでの遅延読み込み用)、これは明らかに役に立ちません。

最も単純なルートは、遅延読み込みを行うヘルパー型である可能性が高く、その型のプライベート フィールドを持ち、パブリック プロパティがヘルパー型の正しいプロパティ/メソッドを呼び出します。

例えば

public class Foo {
  private class LazyLoader {
    private someType theValue;
    public someType Value {
      get {
        // Do lazy load
        return theValue;
      }
    }
  }

  private LazyLoader theValue;
  public someType {
    get { return theValue.Value; }
  }
}

これには、バッキング フィールドがプロパティよりも使いにくいという利点があります。

(問題を解決するための余分なレベルの間接化の別のケース。)

于 2009-02-18T10:54:02.760 に答える
0

現在、私は同様の状況にあります。プロパティアクセサーのみが使用するフィールドがあります。
プロパティが設定されているときに追加のロジックを実行する必要があるため、自動プロパティを使用できません。(プロパティも遅延ロードされません)。

次のバージョンの C# で次のようなことが可能になるとしたら、すばらしいと思いませんか。

public class MyClass
{
    public int MyProperty
    {
        int _backingField;

        get
        {
            return _backingField;
        }
        set
        {
            if( _backingField != value )
            {
                _backingField = value;
                // Additional logic
                ...
            }
        }
    }
}

このような構成では、_backingField変数のスコープはプロパティに限定されます。C# の次のバージョンで同様の言語構造を見たいと思います :)

しかし、残念ながらこの機能は実装されないでしょう: http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=381625

于 2009-04-17T13:34:13.797 に答える
0

これは単純すぎるかもしれませんが、すべてのレイジーを基本クラスに抽象化してみませんか?

 public class LazyFoo{
      private string bar;
      public string Bar{
         get{
             // lazy load and return
         }
         set {
             // set
         }
      }
    }

    public class Foo : LazyFoo{
      // only access the public properties here
    }

不必要な抽象化であるという議論はわかりましたが、バッキング フィールドへのすべてのアクセスを排除する最も簡単な方法です。

于 2009-04-17T13:50:41.947 に答える
0
// option 4
class Foo
{
    public int PublicProperty { get; set; }
    public int PrivateSetter { get; private set; }
}

C# 3.0 の機能により、コンパイラは、リフレクションを使用しない限り、誤ってアクセスできない匿名のプライベート バッキング フィールドを生成します...

編集:遅延インスタンス化

あなたはこのような怠惰を持つことができます:

// I changed this with respect to ShuggyCoUk's answer (Kudos!)
class LazyEval<T>
{
  T value;
  Func<T> eval;
  public LazyEval(Func<T> eval) { this.eval = eval; }
  public T Eval()
  {
      if (eval == null)
          return value;
      value = eval();
      eval = null;
      return value;
  }
  public static implicit operator T(LazyEval<T> lazy) // maybe explicit
  {
    return lazy.Eval();
  }
  public static implicit operator LazyEval<T>(Func<T> eval) 
  {
    return new LazyEval(eval);
  } 
}

これらの暗黙的な変換により、構文が整理されます...

// option 5
class Foo
{
    public LazyEval<MyClass> LazyProperty { get; private set; }
    public Foo()
    {
        LazyProperty = () => new MyClass();
    }
}

また、クロージャーはスコープを運ぶために使用できます。

// option 5
class Foo
{
    public int PublicProperty { get; private set; }
    public LazyEval<int> LazyProperty { get; private set; }
    public Foo()
    {
        LazyProperty = () => this.PublicProperty;
    }
    public void DoStuff()
    {
        var lazy = LazyProperty; // type is inferred as LazyEval`1, no eval
        PublicProperty = 7;
        int i = lazy; // same as lazy.Eval()
        Console.WriteLine(i); // WriteLine(7)
    }
}
于 2009-02-18T10:47:07.910 に答える
0

これは、そもそも起こらないかもしれないミスを設計しようとしているように思えます。基本的には、間違ったことを心配しています。

オプション1 +コメントを使用します:

///<summary>use Bar property instead</summary> 
string bar;

///<summary>Lazy gets the value of Bar and stores it in bar</summary>
string Bar {
    get {
        // logic to lazy load bar  
        return bar; 
    }
}

バッキング変数を使い続ける開発者がいる場合、私は彼らの技術的能力について心配します。

必ずコードを保守しやすいように設計しますが、コードを単純に保つようにしてください。ここで自分で作成するルールは、価値があるよりも面倒です。

それでも本当に心配な場合は、FxCop (または使用しているもの) ルールを作成して、この種のことを確認してください。

于 2009-04-17T13:56:05.007 に答える
-1

オプション 4 (新しい解決策) :

問題が本当に初期化されていない変数を使用できないようにする方法であるかどうかを確認してから、KNOWN INVALID 値で初期化してください。

私は次のように言います:

string str = "SOMETHING_WRONG_HERE";

「str」を使用している人には、何らかの警告が表示されます。

それ以外の場合、ユーザーが「str」を使用できないようにすることが読みやすさなどよりも重要な場合は、オプション 3。

于 2009-02-18T10:49:30.743 に答える