A .NET application doesn’t just contain code and data; it also contains metadata, which is information about data. In .NET, this means that an application contains the code that defines the application and data that describes the code.
An attribute is one type of metadata that can be stored in a .NET application. Other types of metadata contain information about the types, code, assembly, and all other elements stored in your application.
Reflection is the process of retrieving this metadata at runtime. The data can be inspected and used to make decisions.
In this article, you will learn how to use C# attributes in your own code and how to use reflection along with attributes.
- Using C# Attributes
- Applying C# Attributes
- Prebuilt C# Attributes
- Reading C# attributes
- C# Reflection
- Applying C# Reflection with Attributes
- Further Reading
Using C# Attributes
Using attributes is a powerful way to add metadata to an application. Attributes can be added to all kinds of types: assemblies, types, methods, parameters, and properties.
We can make use of attributes at code time, at design time, and at run time.
C# Code Time Attributes
So, for example, we can use attributes during coding to further describe the code that we’re writing. At code time, we can also make use of attributes to control the compilation of our code. For example, we can use attributes to configure conditional compilation.
We can also use attributes to control the debugging experience in Visual Studio. Some of the attributes that we’ll look at include this conditional compilation and debugging attributes.
C# Design Time Attributes
Attributes can be used at design time, for example, if we’re making use of a visual GUI designer. When we drag and drop a button to a new position, this new position could be represented as attributes in some kind of code-behind.
If we’re making use of an integrated development environment, such as Visual Studio, when we make changes to properties in Visual Studio, these changes may be represented as attributes in our code base.
C# Run Time Attributes
Attributes can also provide information at run time. So, for example, if we’re making use of ASP. NET, we might use attributes to define data validation.
At run time, reflection can be used to access attributes that have been applied to code elements and use those attributes and any properties set on those attributes to control the execution of the program at run time.
Applying C# Attributes
How to apply a C# attribute
In C#, you apply an attribute by placing the attribute name in square brackets
 above the declaration that you want the attribute to apply to.
One example of an attribute in the .NET Framework is
SerializableAttribute. This attribute indicates that a type can be serialized. The .NET Framework checks for the existence of this attribute when serializing a type, and it makes sure that all members of the type can also be serialized.
The below example shows how to apply the Serializable attribute.
As you can see, the actual class in the .NET Framework is called SerializableAttribute. By convention, the name is suffixed with Attribute so you can easily distinguish between attributes and other types in the .NET Framework. When using the attribute, however, you can skip the Attribute suffix.
Using multiple C# attributes
A type can have as many attributes applied to it as necessary. Some attributes can even be applied multiple times. For example, you can use the ConditionalAttribute to indicate to the compiler that a method call should be ignored unless a specific compiler option is specified. The below example shows how to apply this attribute.
An attribute can have parameters. Just as with regular types, those parameters can be named an optional. The values set to an attribute can later be inspected at runtime.
Specifying the target of a C# attribute explicitly
An attribute also has a specific target to which it applies. It can be an attribute applied to a whole assembly, a class, a specific method, or even a parameter of a method.
If you look at the AssemblyInfo.cs of a new class library, you can see how the target is explicitly specified.
These attributes are all applied to the current assembly and describe some metadata about the assembly.
Prebuilt C# Attributes
The DebuggerDisplay attribute is available to customize the display of Class, Struct, Delegate, Enum, Field, Property, and Assembly.
Let’s consider the following Person class, which has two properties as FirstName and LastName.
The normal way the class and its properties displayed is as follows.
To add the DebuggerDisplay attribute, you need to refer to System.Diagnostics namespace.
The attribute takes a string value as a parameter.
The following code shows that the Properties are used as parameters enclosed in curly braces.
The display of the class would change like the following.
DebuggerBrowsable is an attribute available to C# language. This attribute is helpful for determining the display of properties and fields.
Let’s consider the following Person class, which has two properties as FirstName and LastName.
Debugging the code displays the usual way of the array name and its values on expanding.
Let’s add the attribute. You need to add a reference to System.Diagnostics namespace.
Notice that, it takes an enum as a parameter. The DebuggerBrowsableState enum has the following values.
Using this attribute other than Property, Index, and Field would give you the error. Now we would add the RootHidden enum type of DebuggerBrowsableState to AbsentDays property.
Notice that, the property name is not displayed and the children are expanded by default.
This is again very useful if you have many properties and you need to check a few of them. It saves time to drill down.
A conditional attribute is a tag used to mark a method or class whose execution depends on the definition of preprocessing identifier.
If specific symbols have not been defined at the instant they are called, then calls to that method or class will be ignored by the compiler.
Let’s consider the following Program class, which has a Display person method.
The conditional attribute takes conditionString as a parameter. Wherein methods are selectively called on the basis of this definition of the symbol.
It instructs the compiler to compile (into Microsoft Intermediate Language) or not, based on a condition - whether or not a specific conditional compilation symbol is defined.
Now if we run the program it will not display any output. However when we can define the symbol in project properties.
Now if we run the program we can see the output
Reading C# attributes
Applying an attribute isn’t that useful if you can’t retrieve it. Luckily, the .NET Framework offers support for reading attributes through a process called reflection.
The System.Attribute class, from which all other attributes inherit, defines some static methods that can be used to see whether an attribute is applied and to get the current instance of an attribute so you can further inspect it.
Suppose that you want to check that a class has the Serializable attribute applied. You can do this by calling the static IsDefined method on Attribute.
When reading attributes, you’ve already looked at some of the functionality that reflection offers. Reflection enables an application to collect information about itself and act on this information. Reflection is slower than normally executing static code. It can, however, give you a flexibility that static code can’t provide.
The most basic example of reflection is getting the current type of an object you have:
System.Int32 as the type of int.
Type is a class in the .NET Framework that you can use to get all kinds of metadata about any given type.
By using Reflection in C#, one is able to find out details of an object, method, and create objects and invoke methods at runtime.
Let’s create a class and invoke its method using reflection.
Invoke Method with parameters using C# reflection
The above class has the
AddNumb method which takes two
int parameters and returns their addition.
You can call this method using reflection like this:
So this is how it work first you get the type information:
Then will get the method’s information:
Finally, we will invoke the AddNumb method:
Things you can achieve with C# reflection
You can use reflection to configure your application by loading an external configuration file and starting services based on it. Your application won’t have to know in advance about the classes that implement those services, as long as they conform to a specific interface or API.
Using reflection you can generate classes and code on the fly, which simplifies certain programming tasks since the programmer does not have to explicitly create all the needed code. For example, See Applying C# Reflection with Attributes.
Reflection is also invaluable for programs that work by examining code. An example of that would be an IDE or a UI designer.
Reflection helps you reduce boilerplate code.
Reflection is handy for defining mini Domain-Specific Languages (DSL) in your code.
Applying C# Reflection with Attributes
Let’s see a problem statement that a typical C# programmer will face converting a datatable into C# user-defined types
For this example, I am creating a simple Student class.
And a DataTable with some data.
Now, I will convert the preceding DataTable into a
List<Student> using Linq.
The above solution works well for the Student class but what if we have more datatables like this, we can have classes for Teachers, Employees, and whatnot. In that case, we have to write the mapping code just like we did for the Student class in the above example.
However, we can use reflection to write a code that converts datatable to user-defined types based on column names and class properties. Let’s see how to achieve this,
Instantiate a type dynamically using reflection
The following are the two functions in which if we pass a DataTable and a user-defined class as a generic parameter it will then return the List of that class with the DataTable data.
Now, we can convert the preceding DataTable like this,
Thus, reflection simplifies the instantiating and mapping task. The programmer does not have to explicitly write all the code to create an object and setting it values that we did previously using LINQ.
Notice, how we used the
typeof operator to get the type of T from a generic method.
If we call the method like this,
ConvertDataTable<Student>(...) our type will be
Student and If we used,
ConvertDataTable<Teacher>(...) our type will be Teacher.
You may have noticed the following line of code as well
The above condition checks if the property name of the user-defined class is the same as datatable on and set that property value based on the matched column name.
The DataTable column’s name and class property name should be the same otherwise this function will not work properly.
Now, what if these names somehow failed to match and you don’t want to rename the class properties as well
As you can see Id property in Student class and Datatable is mismatched.
Let’s tackle the above problem using attributes
Creating custom C# attributes
First, we will define a custom attribute
It only has one property Values which is a string array. Now we will use this attribute on Id property in our Student class like this:
Using reflection to read C# attribute
Now we will write a method that checks whether Values defined on a property match with columnName passed in the parameter, using reflection.
Now we can update our
GetItem method to use the above method like this
Now our program will able to map the properties values even though the name of columns and properties mismatched. This was possible as it takes into consideration the metadata that we attached with the property using attributes.
That’s all for now, join me in the next article by subscribing to my newsletter using the form below.
Generics In C# - This article explains the Generics in C# with the help of a real-time problem and its step by step solution using c# generics. This article serves to be an in-depth post on getting started with generics in c#.
Reserved attributes: Determine caller information - Caller Info Attributes. These attributes tell the compiler to give you information from the caller’s source code. You have to apply the attributes to optional parameters which should have default values defined. The attributes are quite useful for logging information and debugging source code.
Creating Copies of Attributes by Phil Haack - This article explains that you may want to avoid changing the attributes directly, because when you use reflection to retrieve attributes, those attributes may be cached by the framework. So changing an attribute is not a safe operation as you may be changing the attribute for everyone else who tries to retrieve them. Instead what you really wanted to do is create a copy of all these attributes, and pass the collection of copied attributes along.
Subscribe to Code with Shadman
Get the latest posts delivered right to your inbox