我如何获得我的自定义属性被应用的成员?
我在C#中创build一个自定义属性 ,我想根据属性是应用于方法还是属性做不同的事情。 起初,我将在自定义属性构造函数中执行new StackTrace().GetFrame(1).GetMethod()
以查看称为属性构造函数的方法,但现在我不确定那会给我什么。 如果该属性应用于某个属性,该怎么办? GetMethod()
会为该属性返回一个MethodBase
实例吗? 是否有不同的方式获得在C#中应用了属性的成员?
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = true)] public class MyCustomAttribute : Attribute
更新:好吧,我可能一直在问错误的问题。 从自定义属性类中,如何获取应用了我的自定义属性的成员(或包含成员的类)? Aaronaughtbuild议不要在堆栈中find应用了我的属性的类成员,但是如何从我的属性的构造函数中获取这些信息呢?
由于堆栈框架和方法的工作方式似乎存在很多混淆,下面是一个简单的示例:
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#中的属性是语法糖的一种forms。 它们编译成IL中的getter和setter方法,有些.NET语言可能甚至不会将它们识别为属性 – 属性parsing完全按照惯例完成,IL规范中没有任何规则。
现在,让我们假设你有一个非常好的理由让程序想要检查自己的堆栈(而且这样做有很less的实际原因)。 为什么在这个世界上,你会希望它的性质和方法有不同的performance?
属性背后的基本原理是它们是一种元数据。 如果您需要不同的行为,请将其编码到属性中 。 如果一个属性可能意味着两个不同的东西,取决于它是否适用于方法或属性 – 那么你应该有两个属性 。 将第一个目标设置为AttributeTargets.Method
,将第二个目标设置为AttributeTargets.Property
。 简单。
但是再一次,走自己的栈来从调用方法中获取一些属性是最危险的。 从某种意义上说,你正冻结你的程序的devise,使任何人都难以扩展或重构。 这不是通常使用的属性的方式。 一个更合适的例子,就像一个validation属性:
public class Customer { [Required] public string Name { get; set; } }
那么你的validation器代码,它不知道传入的实际实体可以做到这一点:
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); } } }
换句话说,你正在研究一个给你的实例的属性,但是你不一定知道什么types的实例 。 XML属性,数据合约属性,甚至是属性属性 – .NET Framework中的几乎所有属性都以这种方式使用,以实现一些关于实例types而不是关于程序状态的dynamicfunction,或者堆栈上发生了什么事情。 在创build堆栈跟踪的时候,你实际上不太可能控制这个。
所以我会再次推荐你不要使用堆栈式的方法,除非你有非常好的理由这样做,但是你还没有告诉我们。 否则,你很可能会发现自己处于一个受到伤害的世界。
如果你绝对必须(不要说我们没有提醒你),那么使用两个属性,一个可以应用于方法,另一个可以应用于属性。 我想你会发现,比单个超级属性更容易使用。
属性提供元数据,不知道他们正在装饰的东西(类,成员等)。 另一方面,被装饰的东西可以要求它装饰的属性。
如果你必须知道正在装饰的东西的types,你将需要明确地将它传递给你的属性在它的构造函数。
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = true)] public class MyCustomAttribute : Attribute { Type type; public MyCustomAttribute(Type type) { this.type = type; } }
通过调用ICustomAttributeProvider(reflection对象)上的GetCustomAttributes方法来激活自定义属性,该方法表示应用该属性的位置。 所以在属性的情况下,一些代码将获得属性的PropertyInfo,然后调用GetCustomAttributes。
如果你想build立一个validation框架,你需要编写检查自定义属性的types和成员的代码。 你可以例如有一个接口,属性实现参与你的validation框架。 可以像下面这样简单:
public interface ICustomValidationAttribute { void Attach(ICustomAttributeProvider foundOn); }
你的代码可以在(例如)Type:
var validators = type.GetCustomAttributes(typeof(ICustomValidationAttribute), true); foreach (ICustomValidationAttribute validator in validators) { validator.Attach(type); }
(大概你会走整个reflection图,并为每个ICustomAttributeProvider做这个)。 对于.net FX中类似方法的一个例子,你可以看看WCF的“行为”(IServiceBehavior,IOperationBehavior等)。
更新:.net FX确实有一个通用的目的,但是基本上是以ContextBoundObject和ContextAttributeforms存在的非logging拦截框架。 您可以在网上search一些使用AOP的例子。
GetMethod
将始终返回您的函数名称。 如果它是一个属性,您将得到get_PropertyName
或set_PropertyName
。
一个属性基本上是一种方法,所以当你实现一个属性时,编译器会在生成的MSIL中创build两个独立的函数,一个get_和一个set_方法。 这就是为什么在堆栈跟踪中你会收到这些名字。