Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can C# Attributes access the Target Class?

I want to access the properties of a class from the attribute class by using reflection. Is it possible?

For example:

class MyAttribute : Attribute {     private void AccessTargetClass()     {         // Do some operations     } }  [MyAttribute] class TargetClass { } 
like image 371
Selçuk Öztürk Avatar asked Feb 21 '10 23:02

Selçuk Öztürk


People also ask

Does Can-C reverse cataracts?

A popular eye drop 'Can-C' containing N-alpha-acetylcarnosine (NAC) claims to reduce, reverse and slow the development of senile cataract. It was developed and is patented by Professor Babizhayev, a bio-physicist and Executive Director of Innovative Vision Products (IVP) [1].

Is Can-C good for dogs?

SAFE FOR HUMANS AND DOGS - Can-C is the first and only patented NAC eye drop that uses the exact formula proven effective in both animal and human trials, offering a non-invasive alternative to cataract surgery. EVERY BLINK HYDRATES and lubricates the eye and cornea.

What does Can-C do for the eyes?

Can-C's ingredients help to soothe and rejuvenate tired eyes, help with macular degeneration, cataracts, and other age-related eye disorders. Can-C eye drops for cataracts contain lubricants, an antioxidant and an anti-glycating agent, N-Acetyl-Carnosine (NAC).

Can-C NAC?

The main ingredient of Can-C™ NAC eye drops is N-acetylcarnosine (NAC), a special analogue of di-peptide carnosine, a naturally occurring anti-oxidant nutrient. NAC is extremely effective at 'mopping up' free radicals, which accelerate aging in the body, including the growth of cataracts.


2 Answers

There are cases where you can't rely on the calling code to pass a type reference.

Below is a solution I cooked up to solve this problem. It's a bit of a shot-gun approach, but it does work...

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);         }     } } 
like image 87
Mark Avatar answered Oct 20 '22 00:10

Mark


Not directly, but in order for the attribute to do anything, you already must have a handle to the type/member and call its GetCustomAttributes method, so you can simply pass it in from there:

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

Using it like so:

Type classType = typeof(MyClass); object[] attribs = classType.GetCustomAttributes(typeof(MyAttribute), true); if(attribs.Length > 0) {     ((MyAttribute)attribs[0]).DoSomethingWithDeclaringType(classType); } 
like image 30
Rex M Avatar answered Oct 20 '22 00:10

Rex M