Frequently Asked Questions

Below you find a list of Frequently Asked Questions about .NET Attributes and this web site. 

What are attributes?
This still sounds pretty vague. Can you give me some examples?
Are attributes only used at design time in Visual Studio?
So far you only showed C# examples. Can attributes be used in VB.NET as well?
Can I create my own attributes?
Why would it be useful to create my own attributes?
How can I create attributes that accept arguments?
What are named parameters?
Should I use constructors or rely on named parameters?


What are attributes?

Good question. Since this site is all about attributes, a good understanding of the basics of attributes is crucial.

According to this MSDN page, an attribute:

"associates predefined system information or user-defined custom information with a target element. A target element can be an assembly, class, constructor, delegate, enum, event, field, interface, method, portable executable file module, parameter, property, return value, struct, or another attribute. "

Does that help? It depends. If you're familiar with attributes in .NET you probably understand what this is all about. But if you're new to attributes, this may still confuse you.

The easiest way to think of attributes is to consider them as metadata. You can think of them as labels you can stick to other .NET elements like properties, methods, classes, assemblies and more, the same as you would stick a label on your suitcase when you go on holiday, or the way a dead body is tagged in series like CSI: the label adds value (your name or home address in case of the suitcase, the date, time and location the body was found in the second example) to the object it's attached to.

Once you apply an attribute, it can be accessed at run-time, diagnosed and used to base decisions and logic on.


This still sounds pretty vague. Can you give me some examples?

Sure. You'll find many examples in the .NET Framework. Have you ever considered what happens when you drag a Button control on an ASPX Web Form? Where does the markup come from? How does Visual Studio know how to create this markup:

<asp:Button ID="Button1" runat="server" Text="Button" />

The answer is: attributes. In the System.Web dll you find the System.Web.UI.WebControls.Button class whose signature looks like this:

[
  ToolboxData("<{0}:Button runat=\"server\" Text=\"Button\"></{0}:Button>"), 
  DefaultProperty("Text"), 
  DefaultEvent("Click")
  ...
]
public class Button : WebControl, IButtonControl, IPostBackEventHandler{}  

The stuff between the square brackets are attributes: three in this example. The first one tells the Visual Studio designer what markup to create when you add a button to a Web form. The {0} placeholder is replaced with the "asp" tag prefix when you drag the button on a form. Can you guess where this tag prefix comes from? Yep, another attribute, this time one defined at the assembly level:

[assembly: TagPrefix("System.Web.UI.WebControls", "asp")]

The DefaultProperty determines the property that is highlighted automatically in the Properties Grid when you click the button in the designer. And the DefaultEvent causes Visual Studio to write a Click handler for you in the Code Behind of a page when you double click it.

As you can see, the attributes that Microsoft developers added to some of the classes in the .NET Framework help Visual Studio and you work with .NET in an easier, more straightforward way.


Are attributes only used at design time in Visual Studio?

Absolutely not; they are used at run-time as well, in many different scenarios. Consider this attribute for example, also defined in the System.Web assembly:

[assembly: WebResource("TreeView_Simple_NoExpand.gif", "image/gif")]

This attributes enables the .NET framework to retrieve an embedded image from the System.Web assembly at run-time when it's requested with the special webresource.axd file. Without this attribute, the embedded images would not be externally visible.

Do you want more examples? Look at the Flags attribute for enumerations. The classical example is a PizzaTopings enum:

public enum PizzaToppings
{
  TomatoSauce,
  Pepperoni,
  Cheese,
  Onions
}

While this enum is very useful and avoids "magic numbers" in your code, the downside of this enum is that you can only choose one item at a time. Obviously, you want pizzas with a lot more topping. To enable this, you apply the Flags attribute to make this a flaggable enum that can store a bit mask instead of a simple number:

[Flags]
public enum PizzaToppings
{
  TomatoSauce = 1,
  Pepperoni = 2,
  Cheese = 4,
  Onions = 8
}

With this attribute you can now create toppings like this:

myPizza.Toppings = Toppings.TomatoSauce | Toppings.Cheese | Toppings.Onions;

 


So far you only showed C# examples. Can attributes be used in VB.NET as well?

Yes, attributes are supported by .NET, not just by the programming languages on top of it. Instead of the square brackets, VB.NET requires you to use angled brackets. Additionally you need to use an underscore if you want to place the attribute on its own line (this is no longer needed as of Visual Basic 10, part of Visual Studio 2010, currently not released yet). The same examples in VB.NET look like this:

<ToolboxData("<{0}:Button runat=""server"" Text=""Button""></{0}:Button>"), _
   DefaultProperty("Text"), _
   DefaultEvent("Click") > _
Public Class Button ...
<Assembly: TagPrefix("System.Web.UI.WebControls", "asp")> <Assembly: WebResource("TreeView_Simple_NoExpand.gif", "image/gif")> <Flags> _ Public Enum PizzaToppings TomatoSauce = 1 Pepperoni = 2 Cheese = 4 Onions = 8 End Enum

 


Can I create my own attributes?

Certainly; and that would be useful too (see next question). The base class you need to inherit is System.Attribute, the mother of all attributes in .NET. When you define the class, you can tell .NET to which .NET elements the attribute can be applied to. How do you do that? Using another attribute of course! The following creates an attribute that can only be applied to properties (normal ones with backing fields, or automatically implemented properties) by applying the AttributeUsage attribute:

C#
[AttributeUsage(AttributeTargets.Property)]
public sealed class ValidEmailAttribute : ValidationAttribute
{ }

VB.NET
<AttributeUsage(AttributeTargets.[Property])> _
Public NotInheritable Class ValidEmailAttribute
	Inherits ValidationAttribute  

 


Why would it be useful to create my own attributes?

There are many reasons to create your own attributes. Anywhere you have the need to tag a .NET element and use that information in your code, you could consider attributes. For example, the Validation Framework I have described in my 6 part article series on N-Layer design relies heavily on custom attributes that you can stick on properties to define the validation rules. Check out part two of the article series to see real-world examples.


How can I create attributes that accept arguments?

This is a multi step process. First, you need to create public properties on your attribute class. You can then optionally create one or more overloaded constructors that accept arguments that map to these properties. Finally, you can decorate an element in your code with the attribute using one of the overloaded constructors or with named parameters (see also the next question). I'll explain each of the steps below.

Consider an attribute that is used in a fictitious validation framework (for a not-so fictitious implementation of an almost identical concept, take a look here). Let's assume the attribute is used to verify an integer value is between Min and Max. Logically when applying the attribute you want to be able to specify the Min and Max values as they may differ for each implementation. First, you need to give the attribute a Min and a Max property. You can implement them as automatic properties (in C# at least) like this:

[AttributeUsage(AttributeTargets.Property)]
public sealed class ValidRangeAttribute : Attribute
{
  public double Min { get; set; }
  public double Max { get; set; }
}

The next step is to create an overloaded constructors for this attribute class; for example, one that allows a user to set only one value and let the other default to some other value. Note: this step is optional; you don't have to create constructors. If you don't, the compiler will create a default, parameterless one for you.

[AttributeUsage(AttributeTargets.Property)]
public sealed class ValidRangeAttribute : Attribute
{
  /// <summary>
  /// Initializes a new instance of the ValidRangeAttribute class with Max
/// defaulting to int.MaxValue. /// </summary> public ValidRangeAttribute(double min) { Min = min; Max = int.MaxValue; } /// <summary> /// Initializes a new instance of the ValidRangeAttribute class. /// </summary> public ValidRangeAttribute() { } public double Min { get; set; } public double Max { get; set; } }

The final step is now to apply the attribute. Because of the constructors, you can now use four different ways to do so:

  1. Using the default, parameterless constructor
  2. Using the constructor that sets just the Min value
  3. Using the constructor that only accepts named parameters (which in reality is the same as the first one)
  4. Using the constructor that accepts the Min value and some named parameters (which is the same as the second one)

I'll show you the first two here; you can see the others in the next questions about named parameters.

public class Person
{
  ...
  [ValidRangeAttribute()]
  public int ShoeSize
  {
    ...
  }
  ...
}

In its current form, the attribute isn't very useful. Both Min and Max default to 0 making it impossible to enter anything other than 0.

public class Person
{
  ...
  [ValidRangeAttribute(10)]
  public int ShoeSize
  {
    ...
  }
  ...
}

This code calls the overloaded version that accepts the Min value (10) and then sets the Max value to int.MaxValue.

What if you have many properties on an attribute class but don't want to set all of them but just a few? For those scenarios you can use named parameters, discussed next.


What are named parameters?

Because you need to create and initialize the attribute on a single line of code, you can use some special syntax to set specific properties in a constructor-like syntax. Let's say you want to set just the Max value on the ValidRangeAttribute and let Min default to 0. With a normal object you would initialize an instance of a class, and then assign one or more properties like this:

ValidRangeAttribute myAttribute = new ValidRangeAttribute();
myAttribute.Max = 50;

Since you need to apply the attribute in one call, this wont fly. Instead, you simply refer to the parameters by name like this:

public class Person
{
  ...
  [ValidRangeAttribute(Min=10)]
  public int ShoeSize
  {
    ...
  }
  ...
}

While C# allows you to simply use PropertyName=Value, VB.NET requires you to use the := syntax like this:

Public Class Person
  ...
  <ValidRange(Max:=20)> _
  Public Property ShoeSize() As Integer
    Get
      Return _shoeSize
    End Get
    Set(ByVal Value As Integer)
      _shoeSize = Value
    End Set
  End Property
End Class

Of course you can mix the arguments as well, as shown in the following example:

public class Person
{
  ...
  [ValidRangeAttribute(Min=10, Max=20)]
  public int ShoeSize
  {
    ...
  }
  ...
} 

Should I use constructors or rely on named parameters?

It depends. The problem with named parameters is that you can't enforce them individually. This means that you can't enforce specific properties to be set. However, by creating specialized overloaded constructors you can enforce specific values to be passed in, and leave the rest as named parameters. For example, the following attribute enforces you to always supply a Min value:

[AttributeUsage(AttributeTargets.Property)]
public sealed class ValidRangeAttribute : Attribute
{
  public ValidRangeAttribute(double min)
  {
    Min = min;
    Max = int.MaxValue;
  }

  public double Min { get; set; }
  public double Max { get; set; }
}

If you decorate a property with this attribute and only set the Max value through a named parameters you get a compiler error indicating there's no constructor that takes 0 arguments.