【问题标题】:How do I get the member to which my custom attribute was applied?如何获取应用了我的自定义属性的成员?
【发布时间】:2011-01-11 06:06:52
【问题描述】:

我正在 C# 中创建一个custom attribute,我想根据属性是应用于方法还是应用于属性来做不同的事情。起初我打算在我的自定义属性构造函数中执行new StackTrace().GetFrame(1).GetMethod() 以查看调用属性构造函数的方法,但现在我不确定这会给我带来什么。如果将属性应用于属性会怎样? GetMethod() 会为该属性返回 MethodBase 实例吗?在 C# 中获取应用属性的成员是否有不同的方法?

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

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

【问题讨论】:

  • 不想听起来不好,但我不得不问你为什么要找出你的方法应用了什么属性?这是一种强烈的设计气味。也许你应该问问这样做的动机是什么。
  • 您是否考虑过如果您依赖于调用方法的属性,解决方案会变得多么脆弱?所需要的只是某人决定从该方法中重构一些通用代码以破坏它,并且它们可能与您的运行时检查相去甚远,以至于他们不知道为什么它会破坏并且无法弄清楚它出去。除非你正在编写自己的 DI/AOP 框架,否则我觉得这很可怕......
  • 回答您的更新:您不能。属性不包含有关它们所应用的类型的任何信息;他们反过来工作。通常你只会知道一个属性,因为你是从成员的(属性或方法)GetCustomAttribute[s] 方法中获取的。
  • 从我这里添加另一个“无法从这里到达”。
  • 是的 - 我也被卡住了,虽然我不同意想要检测属性的目标是糟糕的设计。考虑一个DescriptionAttribute 的示例,它被子类化以返回一个文化敏感的资源字符串。理想情况下,资源键将源自应用了 DescriptionAttribute 的属性/成员的名称。如果无法检测到目标,属性就无法实现这一点。

标签: c# reflection attributes custom-errors


【解决方案1】:

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

如果您必须知道被装饰事物的类型,则需要在其构造函数中将其显式传递给您的属性。

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

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

【讨论】:

  • 我已经阅读了很多这些信息,但如果它是有效的呢? C# 是否为此提供任何其他选项?就像在我的情况下,我需要验证登录的用户是否是公司经理,但不是一遍又一遍地编写该代码(甚至调用相同的方法),我宁愿创建一个可以处理该问题的自定义属性,并且提供更简洁的代码。谢谢:)
  • @eestein 你在什么背景下?如果您使用的是 Web 应用程序,我会考虑使用 Authorize 属性和 Identity 框架代码。
  • 它是 Web 应用程序的后端。定期上课。当我实例化类时,我有记录的用户信息,我只是更喜欢使用某种注释,而不是每次需要验证时调用一个方法。感谢您的回复
【解决方案2】:

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

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 属性、数据协定属性,甚至 Attribute 属性 - .NET Framework 中的几乎所有属性都以这种方式使用,以实现一些相对于实例类型是动态的功能,而不是相对于到程序的状态或堆栈上发生了什么。在您创建堆栈跟踪时,您实际上不太可能控制它。

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

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

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

    • 那么“GetMethod”是不是用词不当?
    • 嗯,但您实际上并没有在堆栈跟踪中看到属性访问器,对吗?它应该只是调用 GetCustomAttributes..
    • 在堆栈跟踪中看到属性访问器,因为这实际上是在 IL 中调用的方法。
    • 嗯,这真的取决于你如何访问属性,正如@alexdej 指出的那样,当从 GetCustomAttributes 访问时,方法名称甚至不在堆栈跟踪中(在我的测试中)
    • 当从GetCustomAttributes访问what时?堆栈跟踪不是从属性本身完成的,而是从检查属性的代码完成的。属性只是元数据,除非你在 ctor 中做了可怕的无法形容的事情,否则它们不会真正“执行”。
    【解决方案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 的示例。

    【讨论】:

      猜你喜欢
      • 2013-07-27
      • 1970-01-01
      • 2010-10-12
      • 1970-01-01
      • 2010-10-29
      • 1970-01-01
      • 2013-04-16
      • 1970-01-01
      • 2014-02-25
      相关资源
      最近更新 更多