2009-11-16 2 views
4

Пример:Может ли цель атрибута быть получена из экземпляра атрибута?

[AttributeUsage(AttributeTargets.Class)] 
public class MyAttribute : Attribute 
{ 
    public MyAttribute() 
    { 
    Console.WriteLine("I was defined on the {0} class", ????); 
    } 
} 

[MyAttribute] 
public class MyClass 
{ 
} 

Могу ли я заполнить "????" ответить на этот вопрос? Нужно ли мне перебирать все типы в списке известных сборок, чтобы сделать это? Или есть более короткий путь?

+1

Просто исправить небольшую ошибку. Это 'AttributeTargets.Class', а не' AttributeTarget.Class'. – Yogesh

ответ

1
  1. Вы можете найти все атрибуты, применяемые к данному типу.

  2. Я предполагаю, что вы хотите получить список всех типов классов, украшенных MyAttribute. Вы должны сделать это с помощью Reflection, который я предлагаю. - или напишите метод расширения для атрибута для его выполнения.

Если вам нужно сделать то, что вы просили в этом вопросе - я считаю, что что-то не так в вашем дизайне, потому что вы пытаетесь держать информацию одного конкретного типа в пределах атрибута, атрибут может предназначаться. Атрибуты применяются к любому классу при условии, что они настроены на целевые типы классов. Таким образом, практически атрибут не должен содержать информацию обо всех типах, на которые он нацелен.

Хотя не имеет прямого отношения, но вы можете проверить this question, о котором я спросил, ТАК некоторое время назад.

Restrict custom attribute so that it can be applied only to Specifc types in C#?

+0

ага, вопрос. Ты прав. –

+0

Как сказал @curious_geek, вы должны искать загруженные сборки в AppDomain для типов, которым назначен ваш атрибут. Но у него есть давление для поиска в огромных собраниях, которые не ваши. Таким образом, вы можете создать другой атрибут и пометить свои сборки. И просто ищите внутри них. Создайте коллекцию MyAttributeDescriptor, которая описывает целевой тип. –

+0

То, что я пытаюсь сделать, это построить мост между двумя классами. У меня есть структура данных и фабричная структура. Я хочу, чтобы фабрика могла сказать: «Я могу сделать тип X, а для этого использовать метод Y». Когда я использую эту информацию, я буду знать, что такое тип X, и я хочу найти подходящую фабрику. Я знаю, что типичный подход заключается в инверсии этих отношений. Тип X будет иметь атрибут «тип Z может сделать меня», но мои классы данных и фабрики находятся в отдельных сборках, и мои заводы зависят от моих классов данных. Я не хочу переворачивать эту зависимость по многим другим причинам. – Hounshell

0

Вот решение. Не очень, но решение ...

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

Это в значительной степени то, что я сделал. Справа вплоть до skipAssemblies ... /me смотрит через плечо – Hounshell

Смежные вопросы