我想将派生类中声明的Properties的某些后备字段存储在基类中包含的受保护Hashtable中。
此机制在派生类中的使用必须尽可能简单。
因此,我可以使用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.
}
但是,首先,我不能强迫用户使用此模式,其次,要在派生类中编写大量代码,正如我上面提到的,我希望尽可能简单。
最佳答案
MethodBase
和MemberInfo
都不能正确覆盖Equals
和GetHashCode
函数,但是使用默认的RuntimeHelpers.GetHashCode
和RuntimeHelpers.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);
}
}
优点:
如您所愿,这是一个懒惰的初始化。让我们测试一下:
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
请注意,该值在首次访问时已初始化,之后未更改。
属性由编译器进行写保护-
_bar
字段为readonly
,您无权访问Lazy<T>
的内部字段。因此,不会意外使用备用字段。如果尝试尝试,将在类型不匹配时出现编译错误:CS0029无法将类型
System.Lazy<SomeClass>
隐式转换为SomeClass
即使通过
this._bar.Value
访问它,也不会发生任何可怕的事情,并且您将获得正确的值,就像通过this.Bar
属性访问它一样。它更加简单,快捷并且易于阅读和维护。
开箱即用的安全螺纹。
缺点:—(我没有找到)
关于基于
hashtable
的设计的几分钱:您(或将维护您的代码的人)可以偶然(或建议)访问和/或修改整个哈希表或其项,因为它只是通常的私有财产。
Hashtable对性能的影响很小,而获得stacktrace对性能的影响很大。但是我不知道它是否很关键,取决于您访问属性的频率。
很难阅读和维护。
不是线程安全的。
关于c# - MethodBase作为哈希表键,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/38589452/