如何获取应用自定义属性的成员?

Sar*_*els 56 c# reflection attributes custom-errors

我在C#中创建了一个自定义属性,我希望根据属性是应用于方法还是属性来执行不同的操作.起初我打算new StackTrace().GetFrame(1).GetMethod()在我的自定义属性构造函数中查看调用属性构造函数的方法,但现在我不确定这会给我什么.如果属性应用于属性怎么办?会GetMethod()返回MethodBase该属性的实例吗?是否有不同的方法来获取在C#中应用属性的成员?

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property,
    AllowMultiple = true)]
public class MyCustomAttribute : Attribute
Run Code Online (Sandbox Code Playgroud)

更新: 好吧,我可能一直在问错误的问题.在自定义属性类中,如何获取应用了自定义属性的成员(或包含成员的类)? Aaronaught建议不要走向堆栈以找到应用了我的属性的类成员,但是如何从属性的构造函数中获取此信息呢?

Aar*_*ght 41

由于堆栈框架和方法的工作方式似乎存在很多混淆,这里有一个简单的演示:

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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

该计划的输出将是:

set_Name

C#中的属性是一种语法糖.他们在IL中编译为getter和setter方法,有些.NET语言可能甚至不会将它们识别为属性 - 属性解析完全按照惯例完成,IL规范中没有任何规则.

现在,让我们说一下,你有一个非常好的理由让一个程序想要检查它自己的堆栈(并且有很少的实际理由这样做).为什么在世界上你会希望它对属性和方法的表现不同?

属性背后的整个基本原理是它们是一种元数据.如果您想要不同的行为,请将其编码到属性中.如果一个属性可能意味着两个不同的东西,这取决于它是应用于方法还是属性 - 那么你应该有两个属性.将目标设置为第一个AttributeTargets.Method和第二个AttributeTargets.Property.简单.

但是再一次,走你自己的堆栈从调用方法中获取一些属性充其量是危险的.在某种程度上,你冻结了程序的设计,使得任何人扩展或重构都变得更加困难.这不是通常使用属性的方式.一个更合适的例子,就像验证属性:

public class Customer
{
    [Required]
    public string Name { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

然后你的验证器代码,它对传入的实际实体一无所知,可以这样做:

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

换句话说,您正在检查提供给您实例的属性,但您不一定了解有关类型的实例.XML属性,数据协定属性,甚至属性属性 - .NET Framework中的几乎所有属性都以这种方式使用,以实现某些与实例类型相关的动态功能,但不涉及程序状态或什么发生在堆栈上.在创建堆栈跟踪时,您实际上无法控制此操作.

因此,我将再次建议您不要使用堆栈行走方法,除非您有充分的理由这样做,但您还没有告诉我们.否则你很可能会发现自己处于一个受伤的世界.

如果你绝对必须(不要说我们没有警告你),那么使用两个属性,一个可以应用于方法,一个可以应用于属性.我认为你会发现使用它比单个超级属性更容易.


Sco*_*man 40

属性提供元数据,并且不知道他们正在装饰的事物(类,成员等).另一方面,被装饰的东西可以要求它装饰的属性.

如果必须知道要装饰的东西的类型,则需要在构造函数中将其显式传递给属性.

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, 
    AllowMultiple = true)] 
public class MyCustomAttribute : Attribute
{
   Type type;

   public MyCustomAttribute(Type type)
   {
      this.type = type;
   }
}
Run Code Online (Sandbox Code Playgroud)


Ami*_*shk 5

GetMethod将始终返回函数名称。如果它是一个属性,您将获得get_PropertyNameset_PropertyName

属性基本上是一种方法,因此当您实现一个属性时,编译器会在生成的 MSIL 中创建两个单独的函数,即 get_ 和 aa set_ 方法。这就是为什么在堆栈跟踪中您会收到这些名称的原因。


ale*_*dej 5

自定义属性由一些代码调用 ICustomAttributeProvider(反射对象)上的 GetCustomAttributes 方法来激活,该方法表示应用该属性的位置。因此,对于属性,某些代码将获取该属性的 PropertyInfo,然后对其调用 GetCustomAttributes。

如果您想构建一些验证框架,您需要编写检查类型和成员的自定义属性的代码。例如,您可以拥有一个属性实现的接口来参与您的验证框架。可以像下面这样简单:

public interface ICustomValidationAttribute
{
    void Attach(ICustomAttributeProvider foundOn);
}
Run Code Online (Sandbox Code Playgroud)

您的代码可以在(例如)类型上查找此接口:

var validators = type.GetCustomAttributes(typeof(ICustomValidationAttribute), true);
foreach (ICustomValidationAttribute validator in validators)
{
     validator.Attach(type);
}
Run Code Online (Sandbox Code Playgroud)

(大概您会遍历整个反射图并对每个 ICustomAttributeProvider 执行此操作)。有关 .net FX 中类似方法的示例,您可以查看 WCF 的“行为”(IServiceBehavior、IOperationBehavior 等)。

更新:.net FX 确实有一种通用的,但基本上是未记录的拦截框架,以 ContextBoundObject 和 ContextAttribute 的形式。您可以在网上搜索一些将其用于 AOP 的示例。