Skip to content

Contact sales

By filling out this form and clicking submit, you acknowledge our privacy policy.

Using Attributes in C#

Apr 4, 2020 • 6 Minute Read

Introduction

Attributes provide metadata for existing .NET code elements. .NET allows assemblies, types, methods, properties, and other types of elements to be marked with attributes, and these attributes can be queried with reflection. They provide declarative information for code. This article introduces some common attributes and explains how they can be used in C#.

What Is an Attribute?

An attribute is simply an object whose class inherits from the Attribute class. However, attributes are not used in the same way as a typical object. They are often used to hold a small amount of data such as a name or numerical limit, or to toggle behavior on or off. Some attributes specify functionality. For example, an attribute may contain a method that defines security behavior. This behavior may be relevant to two separate methods. The calling code can call the attribute's security method before invoking either method to ensure that both methods are secure.

SerializableAttribute is a prevalent attribute. This attribute tells the .NET XML serialization engine that the class is valid for serialization.

      SerializableAttribute]
public class Example
{
}
    

Notice that it is not necessary to include the "Attribute" suffix. The suffix is merely a convention. The following code achieves the same result as the previous code.

      Serializable]
public class Example
{
}
    

Using Attributes

Attributes apply to one or more different attribute targets. The most common targets are types, such as classes and structs, methods, and properties. Attributes can also apply to events, parameters, constructors, delegates, and more.

Mark a target by typing an open square bracket above the target followed by the attribute name, arguments in brackets, and finally, a closing square bracket. This example from the Newtonsoft JSON library specifies that the FullName property should evaluate at runtime and is excluded from serialization.

      public class Person
{
    public string FirstName { get; set; }
    public string Surname { get; set; }

    [DefaultValue(" ")]
    public string FullName
    {
        get { return FirstName + " " + Surname; }
    }
}
    

Attribute Targets

These are the most common attribute targets, or pieces of code infrastructure that can be marked with an attribute.

TargetDescription
TypeAttributes belong to a delegate, class, struct, interface, or enum
MethodAttributes relate to a method or property accessors (get / set)
PropertyAttributes relate to properties
AssemblyAttributes relate to the entire assembly
FieldAttributes relate to a field on a class or struct
ReturnAttributes relate to the return value of a method or property accessors
EventAttributes relate to an event
ParameterAttributes relate to parameters of methods or property accessors

In the example below, the Id property is the target. It is marked with the Key attribute. The Key attribute doesn't change the behavior of the code directly. This property operates as expected, whether the attribute exists or is removed. However, some libraries, like Entity Framework, pay attention to the attribute. In the case of Entity Framework, the Id property is considered to be the primary key. When the entity is queried, Entity Framework knows that it can obtain an instance of the entity from the database uniquely by the Id column.

      public class Car
{
    [Key]
    public Guid Id { get; set; }
    public string Color { get; set; }
    public string Make { get; set; }
    public string Model { get; set; }
}
    

This code loads the Car entity from the database by primary key with Entity Framework. If the Key attribute is not specified, an exception is thrown.

      var car = await Cars.FindAsync(new Guid("ca4080b2-fa16-45eb-aa1f-21d677378cc8"));
    

In the example below, the assembly title is set. It results in setting the "File description" of the physical DLL file. If you check the details of the compiled DLL, it says "ExampleAssembly".

      using System;
using System.Reflection;

[assembly: AssemblyTitle("ExampleAssembly")]
    

In the example below, the target is a class, and it is marked with the Obsolete attribute. It causes compilation warnings when the class is used. It can be used to encourage developers to use different code. The message specified here shows up in the warnings window.

      Obsolete("This address is no longer used. Please use InternationalAddress instead")]
public class Address
{
    public string StreetNumber { get; set; }
    public string StreetName { get; set; }
    public string City {get;set;}
    public int ZipCode { get; set; }
    public string State { get; set; }
}
    

Attribute Reflection

Reflection is a set of .NET APIs that facilitate retrieving metadata from C# attributes. Reflection is used to retrieve attributes associated with an attribute target. This code calls GetCustomAttributes to list the attribute type names for the Id property.

      public class Car
{
    [Key]
    public Guid Id { get; set; }
}
    
      static void Main(string[] args)
{
    var attributes = typeof(Car).GetProperty("Id").GetCustomAttributes().Select(a => a.GetType());
    Console.WriteLine(attributes.First().Name);
}
    

Output:

KeyAttribute

Define attributes by inheriting from the attribute class. Specify parameters for the attribute by adding them to the constructor of the class. This allows the arguments to be specified at compile time when the attribute is referenced.

Conclusion

Attributes are an essential part of the C# ecosystem. They commonly occur in code and can indirectly change the behavior of code. Attributes can specify metadata about code and affect the results of the compilation. .NET APIs can use attributes and their associated data to modify code execution behavior. To create a custom attribute, create a class that derives from the Attribute class. Use reflection to get the attribute metadata for code elements.