2010-02-21 41 views
41

我想通過使用反射來訪問屬性類中的類的屬性。可能嗎?C#屬性可以訪問目標類嗎?

例如:

class MyAttribute : Attribute 
{ 
    private void AccessTargetClass() 
    { 
     // Do some operations 
    } 
} 

[MyAttribute] 
class TargetClass 
{ 
} 

回答

15
直接

,而是爲了使屬性做任何事情,你已經必須有一個句柄類型/成員並調用其GetCustomAttributes方法,所以你可以簡單地將它傳遞在從那裏:

class MyAttribute : Attribute 
{ 
    public void DoSomethingWithDeclaringType(Type t) 
    { 
    } 
} 

使用它像這樣:

Type classType = typeof(MyClass); 
object[] attribs = classType.GetCustomAttributes(typeof(MyAttribute), true); 
if(attribs.Length > 0) 
{ 
    ((MyAttribute)attribs[0]).DoSomethingWithDeclaringType(classType); 
} 
+2

您也可以在屬性構造函數中提供聲明類型,讓您可以訪問該類型。例如。 ' class MyAttribute:Attribute Private void MyAttribute(Type declaringType) { // Access在此聲明類型,如果需要的話甚至保留一個引用。 } } [MyAttribute(typeof運算(TargetClass))] 類TargetClass { } ' – Jaans

18

有AR在這種情況下,您不能依賴調用代碼來傳遞類型引用。

下面是我熟練解決這個問題的解決方案。這是一個有點炮彈的方法,但它的確工作...

using System; 
using System.Reflection; 
using System.Collections.Generic; 
using System.Linq; 
using System.Diagnostics; 


namespace Ethica.Reflection 
{ 
    /// <summary> 
    /// Helps in discovery of the target of an Attribute 
    /// </summary> 
    /// <typeparam name="TAttribute">An Attribute derived type</typeparam> 
    /// <remarks> 
    /// The .NET framework does not provide navigation from attributes back to their targets, principally for the reason that 
    /// in typical usage scenarios for attributes, the attribute is discovered by a routine which already has a reference to a 
    /// member type. 
    /// 
    /// There are, however, bona-fide cases where an attribute needs to detect it's target - an example is a localizable sub-class of the 
    /// DescriptionAttribute. In order for the DescriptionAttribute to return a localized string, it requires a resource key and, ideally, 
    /// a type reference as the base-key for the ResourceManager. A DescriptionAttribute could not provide this information without 
    /// a reference to it's target type. 
    /// 
    /// Note to callers: 
    /// 
    /// Your Attribute-derived class must implement Equals and GetHashCode, otherwise a run-time exception will occur, since this class 
    /// creates a dictionary of attributes in order to speed up target lookups. 
    /// </remarks> 
    public static class AttributeTargetHelper<TAttribute> 
     where TAttribute : Attribute 
    { 
     /// <summary> 
     /// Map of attributes and their respective targets 
     /// </summary> 
     private static Dictionary<TAttribute, object> targetMap; 

     /// <summary> 
     /// List of assemblies that should not be rescanned for types. 
     /// </summary> 
     private static List<string> skipAssemblies; 

     /// <summary> 
     /// Adds an attribute and it's target to the dictionary 
     /// </summary> 
     /// <param name="attribute"></param> 
     /// <param name="item"></param> 
     private static void Add(TAttribute attribute, object item) 
     { 
      targetMap.Add(attribute, item); 
     } 

     /// <summary> 
     /// Scans an assembly for all instances of the attribute. 
     /// </summary> 
     /// <param name="assembly"></param> 
     private static void ScanAssembly(Assembly assembly) 
     { 
      const BindingFlags memberInfoBinding = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance; 

      if (!skipAssemblies.Contains(assembly.FullName)) 
      { 
       skipAssemblies.Add(assembly.FullName); 

       Debug.WriteLine("Loading attribute targets for " + typeof(TAttribute).Name + " from assembly " + assembly.FullName); 

       foreach (TAttribute attr in assembly.GetCustomAttributes(typeof(TAttribute), false)) 
        Add(attr, assembly); 

       foreach (Type type in assembly.GetTypes()) 
       { 
        foreach (TAttribute attr in type.GetCustomAttributes(typeof(TAttribute), false)) 
         Add(attr, type); 

        foreach (MemberInfo member in type.GetMembers(memberInfoBinding)) 
        { 
         foreach (TAttribute attr in member.GetCustomAttributes(typeof(TAttribute), false)) 
          Add(attr, member); 

         if (member.MemberType == MemberTypes.Method) 
          foreach (var parameter in ((MethodInfo)member).GetParameters()) 
           foreach (TAttribute attr in parameter.GetCustomAttributes(typeof(TAttribute), false)) 
            Add(attr, parameter); 
        } 
       } 
      } 

      foreach (var assemblyName in assembly.GetReferencedAssemblies()) 
      { 
       if (!skipAssemblies.Contains(assemblyName.FullName)) 
        ScanAssembly(Assembly.Load(assemblyName)); 
      } 
     } 

     /// <summary> 
     /// Returns the target of an attribute. 
     /// </summary> 
     /// <param name="attribute">The attribute for which a target is sought</param> 
     /// <returns>The target of the attribute - either an Assembly, Type or MemberInfo instance.</returns> 
     public static object GetTarget(TAttribute attribute) 
     { 
      object result; 
      if (!targetMap.TryGetValue(attribute, out result)) 
      { 
       // Since types can be loaded at any time, recheck that all assemblies are included... 
       // Walk up the stack in a last-ditch effort to find instances of the attribute. 
       StackTrace stackTrace = new StackTrace();   // get call stack 
       StackFrame[] stackFrames = stackTrace.GetFrames(); // get method calls (frames) 

       // write call stack method names 
       foreach (StackFrame stackFrame in stackFrames) 
       { 
        Console.WriteLine(stackFrame.GetMethod().Name); // write method name 
        ScanAssembly(stackFrame.GetMethod().GetType().Assembly); 
       } 

       if (!targetMap.TryGetValue(attribute, out result)) 
        throw new InvalidProgramException("Cannot find assembly referencing attribute"); 
      } 
      return result; 
     } 

     /// <summary> 
     /// Static constructor for type. 
     /// </summary> 
     static AttributeTargetHelper() 
     { 
      targetMap = new Dictionary<TAttribute, object>(); 

      // Do not load any assemblies reference by the assembly which declares the attribute, since they cannot possibly use the attribute 
      skipAssemblies = new List<string>(typeof(TAttribute).Assembly.GetReferencedAssemblies().Select(c => c.FullName)); 

      // Skip common system assemblies 
      skipAssemblies.Add("System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); 
      skipAssemblies.Add("System.Security, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"); 
      skipAssemblies.Add("System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); 
      skipAssemblies.Add("System.Data.SqlXml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); 
      skipAssemblies.Add("System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"); 
      skipAssemblies.Add("System.Numerics, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); 

      // Scan the entire application 
      ScanAssembly(Assembly.GetEntryAssembly()); 
     } 

    } 


    /// <summary> 
    /// Extends attributes so that their targets can be discovered 
    /// </summary> 
    public static class AttributeTargetHelperExtension 
    { 
     /// <summary> 
     /// Gets the target of an attribute 
     /// </summary> 
     /// <typeparam name="TAttribute"></typeparam> 
     /// <param name="attribute">The attribute for which a target is sought</param> 
     /// <returns>The target of the attribute - either an Assembly, Type or MemberInfo instance.</returns> 
     public static object GetTarget<TAttribute>(this TAttribute attribute) 
      where TAttribute : Attribute 
     { 
      return AttributeTargetHelper<TAttribute>.GetTarget(attribute); 
     } 
    } 
} 
+0

感謝您的溶液 – Heka

12

屬性主要目的是註釋:你聲明?應該以這種或那種方式對待類/方法/屬性,但實際分析所有這些東西的代碼應該在其他地方。所以你在屬性內部做某件事的意圖正在打破這種模式。我的建議:考慮另一種解決方案

+0

等待,直到 「DoSomethingWithDeclaringType」 種工程的方法,但它意味着以某種方式強制執行該檢查僅在第一次調用DoSomethingWithDeclaringType時完成,並且這意味着確保沒有人直接查找屬性並在沒有通過批准功能的情況下對其進行操作。如果可以的話,我寧願把這個驗證代碼放入屬性本身,如果可能的話,我認爲這不會違反該模式。 – DMC

+0

假設一個屬性正在建立一個類與另一個類之間的關係。你在哪裏放驗證碼,說:「是的,這兩個類實際上是相互兼容的?」我只想在加載時運行該代碼一次,建立屬性時,我不想依賴每個添加屬性的類,以確保它們將代碼放入以驗證連接。等待直到「DoSomethingWithDeclaringType」種類起作用的方法,但它意味着某種方式強制檢查僅在第一次調用DoSomethingWithDeclaringTyp – DMC