MethodBase作为Hashtable键

时间:2016-07-26 12:06:24

标签: c# properties hashtable backing-field methodbase

我想在基类中包含的受保护的Hashtable中存储在派生类中声明的属性的一些支持字段。 在派生类中使用此机制必须尽可能简单。

那么,我可以使用MethodBase.GetCurrentMethod()来提供有关调用属性的信息(getter - 属性是只读的),因此它可以被识别为唯一可以访问此特定支持字段的属性吗? / p>

修改

基本上,我想实现模式:

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>();

我想这样做的原因是为了简化属性的使用。 我使用私有属性来创建一些对象并在类中使用它们。但是当我将他们的支持字段存储在同一个类中时,我对它们的属性具有相同的访问权限,因此我(意味着将来会创建一些派生类的用户)可能会意外地使用支持字段而不是属性,< strong>所以我想限制对支持字段的访问,同时允许创建对象并使用它。

我尝试在支持字段上使用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.
    }

但是,首先,我不能强迫用户使用这种模式,其次,要在派生类中编写很多代码,如上所述,我希望尽可能简单。

1 个答案:

答案 0 :(得分:2)

MethodBaseMemberInfo都未正确覆盖EqualsGetHashCode个函数,但使用默认RuntimeHelpers.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中不可用,但您可以轻松实现它,甚至可以使用any existing implementation (just replace Func<T> with your delegate)。用法示例:

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. 开箱即用的线程安全。

  5. 缺点: - (我没找到)

    基于hashtable的设计的几分钱:

    1. 您(或将维护您的代码的人)可能会意外(或建议)访问和/或修改整个哈希表或其项目,因为它只是一般的私有财产。
    2. Hashtable是一个轻微的性能影响+获得stacktrace是一个巨大的性能影响。但是我不知道它是否至关重要,取决于您访问房产的频率。
    3. 很难阅读和维护。
    4. 不是线程安全的。