2

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

那么,我可以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>();

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

我尝试在支持字段上使用 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也不MemberInfo正确地覆盖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字段 isreadonly并且您无权访问Lazy<T>. 因此,没有任何意外的支持字段使用。如果你尝试你会得到类型不匹配的编译错误:

    CS0029 无法将类型隐式转换System.Lazy<SomeClass>SomeClass

    即使您通过 访问它this._bar.Value,也不会发生任何可怕的事情,并且您将获得正确的值,就像您通过this.Bar属性访问它一样。

  3. 它更简单、更快、更容易阅读和维护。

  4. 开箱即用的线程安全。

缺点:—(没找到)

hashtable关于您的基于设计的几美分:

  1. 您(或将维护您的代码的人)可能会意外(或明智地)访问和/或修改整个哈希表或其项目,因为它只是通常的私有财产。
  2. Hashtable 对性能的影响很小 + 获取 stacktrace 对性能的影响很大。但是我不知道这是否重要,取决于您访问属性的频率。
  3. 这将很难阅读和维护。
  4. 不是线程安全的。
于 2016-07-26T12:49:09.947 回答