#473 – Specifying what Type of Elements a Custom Attribute Can Be Applied To

When you define a custom attribute, you can attach it to a variety of program elements by default.  But if your attribute is meant to apply to only one (or more) elements, you can specify that when you define the attribute.

You define which elements your custom attributes can be attached to by specifying the AttributeUsage attribute along with the definition of your attribute.  You specify one or more AttributeTargets values.  (Intellisense will provide a list, as shown below).

In the example below, I’ve specified that the PopCulture attribute may be used on parameters, properties and fields.

    [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Field)]
    public sealed class PopCultureAttribute : Attribute
    {

If we try attaching the attribute to an unsupported element, like a method, we get an error at compile time.

#472 – Attributes Can Be Attached to a Variety of Elements

Custom attributes can be attached not only to class methods, but to a number of different types of elements.

You can attach an attribute to any of the following elements:

  • An assembly
  • A class
  • A constructor
  • A delegate
  • An enumerated type
  • An event
  • A field
  • A generic parameter
  • An interface
  • A method
  • A module (.dll or .exe)
  • A parameter
  • A property
  • A return value
  • A struct

Below is an example of attaching custom attributes to parameters.

    public sealed class PopCultureAttribute : Attribute
    {
        public string Trivia;
        public object SuggestedValue;

        public PopCultureAttribute(string trivia, object suggestedValue)
        {
            Trivia = trivia;
            SuggestedValue = suggestedValue;
        }
    }

 

        public void FeedCowInBarn(
            [PopCulture("Cows stomachs can bloat when feeding them rich grains", "Silage")] string feedType,
            [PopCulture("Cows spend 6 hrs/day eating", 60)] int numMinutes)
        {
            Console.WriteLine("Cow eats slop in dim confines of barn.  {0}, {1}", feedType, numMinutes);
        }

#471 – Reading the Value of an Attribute Using Reflection

The purpose of creating a custom attribute is so that some tool or application code can read the data at runtime and make use of it.  You can use reflection to read custom attribute data at runtime.

You use the static Attribute.GetCustomAttribute method to retrieve an object representing your custom attribute, from a specific type member.

In the example below, I iterate through all of the methods of the Cow class and then call GetCustomAttribute to see which ones have an attached MethaneFootprintAttribute.

foreach (MethodInfo mi in typeof(Cow).GetMethods())
{
    Console.WriteLine("Method Cow.{0}", mi.Name);

    MethaneFootprintAttribute methAtt =
        (MethaneFootprintAttribute) Attribute.GetCustomAttribute(mi, typeof(MethaneFootprintAttribute));

    if (methAtt != null)
        Console.WriteLine("  - Methane: {0}", methAtt.kgMethane);
}

#470 – Defining Your Own Custom Attribute

You can use predefined attributes to attach metadata to type members.

You can also define a custom attribute by creating a new class inheriting from System.Attribute.  The class name must end in “Attribute”.  You typically define a constructor that takes arguments that consist of the metadata that you want to attach to the type member.

    /// <summary>
    /// Attach to a class method to indicate kg of methane that is
    /// output when calling the method.
    /// </summary>
    public class MethaneFootprintAttribute : Attribute
    {
        public double kgMethane;

        public MethaneFootprintAttribute(int kg)
        {
            kgMethane = kg;
        }
    }

You can use the new attribute to attach metadata to individual type members.  You use the name of the new class, without the trailing “Attribute”.

        [MethaneFootprint(45)]
        public void FeedCowInBarn()
        {
            Console.WriteLine("Cow eats slop in dim confines of barn");
        }

        [MethaneFootprint(29)]
        public void LetGrazeOutside()
        {
            Console.WriteLine("Cow enjoys grazing and ends up healthier");
        }

#468 – Attributes Allow Adding Metadata to Program Elements

You can use an attribute to specify metadata that you want attached to some element in your application.  There are a number of predefined attributes in the .NET Framework, for various purposes.  You can also define your own custom attributes.

You can find a list of all predefined attributes here.

Here are a few examples of predefined attributes that you might use:

  • DebuggerDisplay – Tells Visual Studio how to display summary information about a type in a window that shows the current value of an object
  • Obsolete – Indicates that a method is obsolete and should no longer be used (issues a warning at compile time)
  • Serializable – Indicates that a type can be serialized (stored as a sequence of bytes).
  • WebMethod – Indicates that a method can be used through an XML web service