C#

C# Attributes – introduction

C# Attributes are very popular for ‘decorating’ assembly with some additional data, usually influencing on the application’s behavior or the way data are managed. You probably already used at least some of them – Obsolete, Serializable, Required, Display, MaxLength, RegularExpression, WebMethod are only a few of hundreds available in .Net world. What is more, you can write your own custom attribute and use it like a build-in one. I will write more about this in the next post. Right now, let’s focus on…

What are C# Attributes?

Attributes are just classes that deliver metadata (shortly – some extra data) to your assembly (a compiled project, ex a .dll or .exe file). Adding some of these data would be difficult so attributes comes here for the rescue! Of course, given attributes can be later read with the help of reflection so the application can ‘decide’ how to use this additional data. Sounds interesting? Yeah, but stay cool and remember that attributes are not a part of your application model. They may be more useful at a compile-time than at a run-time. They are just a description of your code/data structures and definitely are not an app’s domain.
Having that in mind, let’s look at a real-life situation. When we create an application, we get the assembly information set for us. The data are stored in AssemblyInfo.cs file and its content may look like this:

using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

[assembly: AssemblyTitle("Demo")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Demo")]
[assembly: AssemblyCopyright("Copyright © 2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: Guid("0191ece6-dea5-461a-8e39-e5fb2a6db0ef")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

How are these data used? You can find them by displaying Assembly Information window in Visual Studio (right click on the project -> Properties -> Application (on the left pane) -> Assembly Information (button)).

 

Build-in attributes – how to use them?

Using C# attributes is very easy and you probably did it many times, without even realizing it. But as a reminder, let me write down some of the “rules”.

  1. You add an attribute name (or call its constructor) always in [] brackets.

    For example:

    [Required]
    [Obsolete]
    [ObsoleteAttribute]
    [Serializable]
    [assembly: AssemblyTitle("Demo")]
  2. You add an attribute just before the element it applies to.

    Attribute can apply to the whole type (class, struct, enum) or just a property. Or a field. Or a method. Or… just take a look at the list! ?

    • All
    • Assembly
    • Class
    • Constructor
    • Delegate
    • Enum
    • Event
    • Field
    • GenericParameter
    • Interface
    • Method
    • Module
    • Parameter
    • Property
    • ReturnValueStruct

    Although the above list is quite long, it depends on the exact attribute, on which element we will apply it. Some of the attributes can be placed “everywhere” (All), some of them only before a class (Class). Other can be attached to more exotic places in our code – like a ReturnValue. You may think how to apply an attribute to a result of some method (you do, right?)? Here is an example:

    public class MethodAttributeExample
    {
       [return:SomeAttribute("If parameter is >-5, the result is a positive number")]
       public int SomeMethod(string x)
       {
          return Convert.ToInt32(x) + 5;
       }
    }

    What about an attribute set on a method’s parameter? A piece of cake!

    public int SomeMethod([InfoAttribute("This parameter should be a number!")]string x)
    {
       return Convert.ToInt32(x) + 5;
    }
    

    Mixing the two above? Yeah, just hold my beer!

    [return: InfoAttribute("If parameter is >-5, the result is a positive number")]
    public int SomeMethod([InfoAttribute("This parameter should be a number!")]string x)
    {
       return Convert.ToInt32(x) + 5;
    }

    As you probably realized, I added the attribute that apply to the result of the method, just above the method. So it means I lied a little bit earlier by saying we have to add attribute just before the object it applies to. That’s the most popular way, but in some situation, it is easier to add an attribute just before the parent element and to add a prefix. When prefixes are considered – we have here a really big choice – ‘method’, ‘return’, ‘param’, ‘assemly’, ‘module’, ‘field’ are only a few (and here is the full list)!

  3. When applying a few attributes to the object, put them in one [] brackets (separated with coma) or in separate [] brackets.

    Like in the following example:

    [Author("John Bravo"), Obsolete]
    public int OtherMethod(string x)
    {
       return (Convert.ToInt32(x) + 5);
    }
    
    [Author("John Bravo")]
    [Obsolete]
    public int ThirdMethod(string x)
    {
       return (Convert.ToInt32(x) + 5);
    }

     

  4. Some attributes can be inherited, so you apply it only once.

    In fact, it depends on the attribute. Attributes can have other attributes applied too (they are just classes, so it’s legit), so inheritance is turned on by applying [AttributeUsage] with Inherited set to true. What does it mean?

    Let’s say we have a class that inherits from other class. We can apply the attribute only to parent class and it will be set to the child one. A simple example:

    [AttributeUsage(AttributeTargets.All, AllowMultiple =false, Inherited = true)]
    public class AuthorAttribute : Attribute
    { ... }
    
    [ObsoleteAttribute]
    [Author("Brad Pitt")]
    public class Parent
    { ... }
    
    public class Child : Parent
    {
       //Class has attribute Author. Doesn’t have attribute Obsolete (this one is not inherited).
    }
  5. The full name of attribute is not required. Skip the ‘Attribute’ suffix.

By convention, attributes’ names (understood as classes’ names) usually has suffix ‘Attribute’. Of course it’s just a convention, some developers may not respect it. But remember that decorating your class with [ObsoleteAttribute] will work the same as if you write [Obsolete]!

What’s next?

In this post we talked about C# attributes and how to apply them. Although some of the above are not build-in, the rule of applying them is still the same.
In the next post, I will focus on custom attributes and the reason we use C# attributes. Later, I will show you how to make use of the attributes (with a little help of magic called reflection). So, if you find this topic interesting, stay tuned and drop in soon!

Fetured image byAnnie Spratt

Share this: