我正在C#中创建自定义属性,并且我想根据该属性是否应用于方法还是属性来做不同的事情。最初,我将new StackTrace().GetFrame(1).GetMethod()在自定义属性构造器中进行操作,以查看称为属性构造器的方法,但是现在我不确定会给我带来什么。如果将属性应用于属性怎么办?会为该属性GetMethod()返回一个MethodBase实例吗?是否有另一种方法来获取在C#中应用了属性的成员?
new StackTrace().GetFrame(1).GetMethod()
GetMethod()
MethodBase
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = true)] public class MyCustomAttribute : Attribute
更新: 好的,我可能一直在问错问题。从自定义属性类中,如何获取应用了自定义属性的成员(或包含该成员的类)? Aaronaught建议不要在堆栈上查找要应用我的属性的类成员,但是我还如何从我的属性的构造函数中获取此信息?
由于在堆栈框架和方法的工作方式方面似乎存在很多困惑,因此下面是一个简单的演示:
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规范中实际上没有任何规则。
现在,让我们暂时说一下,您的程序确实有充分的理由要检查其自己的堆栈(并且 很少有 实际的理由要这样做)。为什么在世界上,您希望它对属性和方法的行为有所不同?
属性背后的全部理由是它们是一种元数据。如果您想要其他行为,请将其编码 为属性 。如果一个属性根据它是应用于方法还是属性而具有两种不同的含义- 那么您应该具有 两个属性 。将第一个目标设置为AttributeTargets.Method,第二个目标设置为AttributeTargets.Property。简单。
AttributeTargets.Method
AttributeTargets.Property
但是再次重申,遍历自己的堆栈以从调用方法中获取一些属性充其量是危险的。从某种意义上说,您正在冻结程序的设计,这使任何人都难以扩展或重构。这不是通常使用属性的方式。一个更 合适的 示例将是诸如validation属性之类的东西:
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属性,数据协定属性,甚至是属性属性-.NET Framework中的几乎所有属性都以这种方式使用,以实现某些功能,这些功能相对于 实例 的 类型 是动态的,而不是相对于 程序 的 状态 或堆栈上发生了什么。在创建堆栈跟踪时,实际上不太可能由您来控制。
因此,我将再次建议您 不要 使用堆栈遍历方法,除非您有非常充分的理由这样做(而您尚未告诉我们)。否则,您很可能会陷入痛苦的世界。
如果您绝对必须(不要说我们没有警告您),则使用两个属性,一个可以应用于方法,另一个可以应用于属性。我认为您会发现,与单个超级属性相比,使用它要容易得多。