由于堆栈框架和方法的工作方式似乎存在很多混淆,这里有一个简单的演示:
static void Main(string[] args)
{
MyClass c = new MyClass();
c.Name = "MyTest";
Console.ReadLine();
}
class MyClass
{
private string name;
void TestMethod()
{
StackTrace st = new StackTrace();
StackFrame currentFrame = st.GetFrame(1);
MethodBase method = currentFrame.GetMethod();
Console.WriteLine(method.Name);
}
public string Name
{
get { return name; }
set
{
TestMethod();
name = value;
}
}
}
该程序的输出将是:
set_Name
C# 中的属性是一种语法糖。它们编译为 IL 中的 getter 和 setter 方法,并且某些 .NET 语言甚至可能不将它们识别为属性 - 属性解析完全按照惯例完成,IL 规范中实际上没有任何规则。
现在,让我们暂时假设您有一个非常好的理由让程序想要检查自己的堆栈(并且这样做的实际理由很少)。为什么你会希望它在属性和方法上表现得不同呢?
属性背后的全部原理是它们是一种元数据。如果您想要不同的行为,请将其编码到属性中。如果一个属性可能意味着两个不同的东西,这取决于它是应用于方法还是属性 - 那么你应该有两个 attributes。将目标设置在第一个 toAttributeTargets.Method
和第二个 to AttributeTargets.Property
。简单的。
但是再一次,遍历你自己的堆栈以从调用方法中获取一些属性充其量是危险的。在某种程度上,您正在冻结程序的设计,使任何人都难以扩展或重构。这不是通常使用属性的方式。一个更合适的例子,类似于验证属性:
public class Customer
{
[Required]
public string Name { get; set; }
}
然后你的验证器代码,它对传入的实际实体一无所知,可以这样做:
public void Validate(object o)
{
Type t = o.GetType();
foreach (var prop in
t.GetProperties(BindingFlags.Instance | BindingFlags.Public))
{
if (Attribute.IsDefined(prop, typeof(RequiredAttribute)))
{
object value = prop.GetValue(o, null);
if (value == null)
throw new RequiredFieldException(prop.Name);
}
}
}
换句话说,您正在检查一个实例的属性,该实例已提供给您,但您不一定知道其类型。XML 属性、数据协定属性,甚至 Attribute 属性 - .NET Framework 中的几乎所有属性都以这种方式使用,以实现一些与实例类型相关但与程序状态无关的动态功能或堆栈上发生了什么。在创建堆栈跟踪时,您实际上不太可能控制它。
因此,我将再次建议您不要使用堆栈遍历方法,除非您有充分的理由这样做,而您还没有告诉我们。否则,您可能会发现自己处于一个受伤的世界。
如果你绝对必须(不要说我们没有警告你),那么使用两个属性,一个可以应用于方法,一个可以应用于属性。我想你会发现它比单个超级属性更容易使用。