方法库作为哈希表键

本文关键字:哈希表 方法库 | 更新日期: 2023-09-27 17:58:36

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

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

编辑:

基本上,我想实现模式:

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

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

我尝试在支持字段上使用过时属性,如下所示:

    [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.
    }
但是,首先,我

不能强迫用户使用这种模式,其次,在派生类中编写的代码很多,正如我上面提到的,我希望尽可能简单。

方法库作为哈希表键

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

通过反射限制对某些成员的访问不是一个好主意,因为其他受信任的代码可以使用反射来访问检查侧翼的其他私有数据。考虑通过重新设计类来限制访问。

另请查看代码访问安全性。

根据您的编辑进行更新

您告知您的属性是只读的。我想,简单地将它们声明为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. 哈希表对性能的影响很小 + 获取堆栈跟踪是一个巨大的性能影响。但是我不知道这是否至关重要,取决于您访问房产的频率。
  3. 它很难阅读和维护。
  4. 不是线程安全的。