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

时间:2010-01-30 18:34:53

标签: c# reflection attributes custom-errors

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

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

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

4 个答案:

答案 0 :(得分: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;
        }
    }
}

该计划的输出将是:

  

set_Name

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

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

属性背后的全部基本原理是它们是一种元数据。如果您想要不同的行为,请将编码到属性中。如果一个属性可能意味着两个不同的东西,这取决于它是否应用于方法或属性 - 那么你应该有两个属性。将第一个目标设置为AttributeTargets.Method,将第二个目标设置为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属性,数据协定属性,甚至属性属性 - .NET Framework中的几乎所有属性都以这种方式使用,以实现某些与实例类型相关的动态功能但不尊重到程序的状态或者堆栈上发生了什么。在创建堆栈跟踪时,您实际上不太可能控制它。

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

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

答案 1 :(得分:40)

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

如果你必须知道正在装饰的东西的类型,你需要在构造函数中明确地将它传递给你的属性。

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

   public MyCustomAttribute(Type type)
   {
      this.type = type;
   }
}

答案 2 :(得分:4)

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

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

答案 3 :(得分:4)

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

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

public interface ICustomValidationAttribute
{
    void Attach(ICustomAttributeProvider foundOn);
}

您的代码可以在(例如)类型:

上查找此接口
var validators = type.GetCustomAttributes(typeof(ICustomValidationAttribute), true);
foreach (ICustomValidationAttribute validator in validators)
{
     validator.Attach(type);
}

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

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