2

基本クラスに含まれる保護された Hashtable の派生クラスで宣言されたプロパティのバッキング フィールドをいくつか格納したいと考えています。派生クラスでのこのメカニズムの使用は、できるだけ単純にする必要があります。

では、呼び出しプロパティ (getter - プロパティは読み取り専用) に関する情報を提供するために使用できるので、MethodBase.GetCurrentMethod()この特定のバッキング フィールドにアクセスできる唯一のプロパティとして認識できますか?

編集:

基本的に、私はパターンを実装したい:

private SomeClass _someProperty = null;
private SomeClass SomeProperty
{
    if (_someProperty == null)
    {
        _someProperty = new SomeClass();
    }
    return _someProperty;
}

次のようになります。

private SomeClass SomeProperty
{
    return GetProperty(delegate
    {
        var someProperty = new SomeClass();
        return someProperty;
    };
}

そして基本クラスで

    private System.Collections.Hashtable _propertyFields = new System.Collections.Hashtable();

    protected T GetProperty<T>(ConstructorDelegate<T> constructorBody)
    {
        var method = new System.Diagnostics.StackFrame(1).GetMethod();
        if (!_propertyFields.ContainsKey(method))
        {
            var propertyObject = constructorBody.Invoke();
            _propertyFields.Add(method, propertyObject);
        }
        return (T)_propertyFields[method];
    }

    protected delegate T ConstructorDelegate<T>();

これを行う理由は、プロパティの使用を簡素化するためです。プライベート プロパティを使用していくつかのオブジェクトを作成し、それらをクラスで使用します。しかし、それらのバッキング フィールドを同じクラスに格納すると、プロパティと同じようにそれらにアクセスできるため、(将来いくつかの派生クラスを作成するユーザーを意味する) 誤ってプロパティの代わりにバッキング フィールドを使用する可能性がありますオブジェクトの作成と使用を許可しながら、バッキングフィールドへのアクセスを制限したかったのです。

次のようなバッキング フィールドで ObsoleteAttribute を使用しようとしました。

    [Obsolete("Don't use this field. Please use corresponding property instead.")]
    private SomeClass __someProperty;
    private SomeClass _someProperty
    {
#pragma warning disable 0618 //Disable Obsolete warning for property usage.
        get
        {
            if (__someProperty== null)
            {
                __someProperty = new SomeClass();
            }
            return __someProperty ;
        }
#pragma warning restore 0618 //Restore Obsolete warning for rest of the code.
    }

しかし、第一に、ユーザーにこのパターンの使用を強制することはできません。第二に、派生クラスに記述するコードが多すぎるため、上で述べたように、できるだけ単純にしたいと考えています。

4

1 に答える 1

2

とを適切にオーバーライドすることも、機能させることもせず、デフォルトMethodBaseのと を使用します 。したがって、同じインスタンスのみを比較できますが、同じコンテンツは比較できません。ほとんどの場合、インスタンスを再利用するランタイム キャッシュとしてはこれで十分です。しかし、これが安定して動作するという保証はありません。MemberInfoEqualsGetHashCodeRuntimeHelpers.GetHashCodeRuntimeHelpers.Equals

メタデータを扱うときは、メタデータを一意に識別するものを使用してください。たとえば、MemberInfo.MetadataToken. 独自の比較子を作成して、ハッシュテーブル内で使用できます。

public class MethodBaseComparer : IEqualityComparer<MethodBase>
{
    public bool Equals(MethodBase x, MethodBase y)
    {
        if (ReferenceEquals(x, y))
            return true;

        if (ReferenceEquals(x, null) || ReferenceEquals(y, null))
            return false;

        return x.MetadataToken.Equals(y.MetadataToken) &&
               x.MethodHandle.Equals(y.MethodHandle);
    }

    public int GetHashCode(MethodBase obj)
    {
        return (obj.MetadataToken.GetHashCode() * 387) ^ obj.MethodHandle.GetHashCode();
    }
}

他の信頼できるコードがリフレクションを使用して、チェックの外側にある他のプライベート データにアクセスできるため、リフレクションを介したアクセスを一部のメンバーに制限することはお勧めできません。クラスを再設計してアクセスを制限することを検討してください。

Code Access Securityもご覧ください。

編集に従って更新します。

あなたのプロパティは読み取り専用であると言いました。私は、単にそれらを宣言することreadonlyはあなたの選択肢ではないと思います。プロパティ値の初期化を遅らせたいようです。その場合、それらを として宣言することはできませんreadonly。右?

または多分あなたはできますか?

Lazy<T>クラスを見てください。dotnet 2.0 では使用できませんが、簡単に実装するか、既存の実装を使用することもできます (デリゲートに置き換えるだけFunc<T>です) 。使用例:

public class Foo
{
    private readonly Lazy<int> _bar = new Lazy<int>(() => Environment.TickCount, true);
    //            similar to your constructorBody - ^^^^^^^^^^^^^^^^^^^^^^^^^^^

    private int Bar
    {
        get { return this._bar.Value; }
    }

    public void DoSomethingWithBar(string title)
    {
        Console.WriteLine("cur: {0}, foo.bar: {1} <- {2}",
                          Environment.TickCount,
                          this.Bar,
                          title);
    }
}

長所

  1. あなたが望むように、それは怠惰な初期化です。テストしてみましょう:

    public static void Main()
    {
        var foo = new Foo();
    
        Console.WriteLine("cur: {0}", Environment.TickCount);
    
        Thread.Sleep(300);
        foo.DoSomethingWithBar("initialization");
    
        Thread.Sleep(300);
        foo.DoSomethingWithBar("later usage");
    }
    

    出力は次のようになります。

    cur: 433294875
    cur: 433295171, foo.bar: 433295171 <- initialization
    cur: 433295468, foo.bar: 433295171 <- later usage
    

    値は最初のアクセス時に初期化され、後で変更されないことに注意してください。

  2. プロパティはコンパイラによって書き込み保護されています -_barフィールドはreadonlyであり、 の内部フィールドにはアクセスできませんLazy<T>。そのため、誤ってバッキング フィールドを使用することはありません。試してみると、型の不一致でコンパイル エラーが発生します。

    CS0029 型System.Lazy<SomeClass>を暗黙的に変換できませんSomeClass

    また、 経由でアクセスしてもthis._bar.Value、何もひどいことは起こらず、プロパティ経由でアクセスした場合と同じように正しい値が得られthis.Barます。

  3. はるかに単純で、高速で、読み取りと保守が簡単です。

  4. 箱から出して安全をスレッド化します。

短所: — (私は見つけられませんでした)

hashtableに基づいた設計について数セント:

  1. あなた (またはあなたのコードを維持する人) は、ハッシュテーブル全体またはそのアイテムに誤って (またはアドバイスに従って) アクセスしたり、変更したりする可能性があります。これは、通常のプライベート プロパティであるためです。
  2. Hashtable は小さなパフォーマンス ヒットです + スタック トレースの取得は大きなパフォーマンス ヒットです。ただし、重要かどうかはわかりません。プロパティにアクセスする頻度によって異なります。
  3. 読んで維持するのは難しいでしょう。
  4. スレッドセーフではありません。
于 2016-07-26T12:49:09.947 に答える