Learn To Understand Reflection in .NET
Reflection is used to dynamically create an instance of a type, bind the type to an existing object, or get the type from an existing object. In this tutorial about reflection in .NET I will break it down into human-readable content with examples.
How to use reflection in .NET? It’s a question many new developers are asking them self and I totally get that, I did that myself at the beginning of my programming career. When you are done reading this article, you will be able to define what reflection in .NET is and how you can utilize it. You will also be able to tell the difference between System.Type
and type
(s). We will cover a few practical examples to make it easier to understand how we can use reflection to get the Type
of an object
or a type
. We will also cover how to interact with Type objects and how to access custom attributes
.
I have tried to “convert” the official documentation about reflection in .NET to a more “human-readable” article. If you take a look below you can see a short description of what .NET reflection is and its roadmap.
If you are ready, then let’s get started and learn how we can use reflection in .NET.
What is Reflection in .NET?
Reflection is a way for developers to get System.Type
instances. These instances are then used to describe assemblies, types, and modules. We can even use an instance to invoke a method and access the variables of its target objects. In other words – reflection gives you the option to use code that was not available at compile time.
Reflection can be used to find all the types in an assembly and then invoke the methods inside that assembly. This gives us the option to dynamically create an instance of a type, and bind that type to an existing object. Another option is to get the type from another already existing object and invoke its methods or access its fields and properties. Reflection also gives us the option to access the attributes
of our objects and their data. In other words, Reflection in .NET and other programming languages is a powerful tool when we don’t know much about an assembly we would like to integrate with.
Reflection Roadmap
System.Type | System.Reflection |
---|---|
Assembly | |
GetType() | Module |
InvokeMember() | AssemblyName |
GetInterface(), GetInterfaces() | InterfaceInfo |
GetContructor(), GetConstructors() | ConstructorInfo |
FindMembers() | ParameterInfo |
GetEvent(), GetEvents() | EventInfo |
GetMember(), GetMembers() | MemberInfo |
GetMethod(), GetMethods() | MethodInfo |
GetField(), GetFields() | FieldInfo |
GetProperty(), GetProperties() | PropertyInfo |
The class System.Type
and namespace System.Reflection
got a very important role when talking about Reflection in .NET. They work in close collaboration and are the ways we achieve reflection inside our application. Above is a table showing how they are related.
An introduction to System.Reflection
System.Reflection
is a namespace and it contains classes and interfaces. These provide a way to get a managed view of fields, methods, and types as well as the option for us to invoke the types dynamically.
The process described above is what we know as reflection in .NET. In the table above you will see some of the classes marked with bold text. This is because they are the most used classes and I have included a short description of each below, for your reference.
Assembly
This of course represents an assembly. An assembly is a self-describing, versionable, and reusable component of a common language when it’s running (also named runtime). The assembly class will contain a number of methods allowing you to load, investigate and manipulate the assembly.
Module
We can use this class to perform reflection on a module when we access the module inside the assembly.
AssemblyName
The assembly name is responsible for allowing us to get information about the assembly. These could be values like:
- The supported culture.
- Versioning.
- Naming.
- Cryptographic key pair.
ParameterInfo
ParameterInfo
is used to discover the attributes of a parameter and provide access to the metadata for the given parameter. You can use an instance of ParameterInfo
to obtain information about the data type, default value, and so on for the parameter, you are working with. If you call GetParameters you will get an array of ParameterInfo
objects representing the parameters of a method.
EventInfo
The class EventInfo
is used to contain information for a given event. We can use this class to discover the attributes of an event and provide access to the event metadata. I typically use this class to inspect events and bind my event handlers.
MemberInfo
MemberInfo
is an abstract base class for classes used to obtain information about all attributes of a member/a class. Here I am talking about:
- Fields.
- Properties.
- Constructors.
- Methods.
- Events.
MemberInfo
introduces the basic functionality that all members provide.
MethodInfo
MemberInfo
is used to discover the attributes of a method and provides access to method metadata. The MethodInfo class represents a method of a type. You can use a MethodInfo object to obtain information about the method that the object represents and to invoke the method.
FieldInfo
Fields are variables we define in a class. FieldInfo is providing us access to the metadata for a given field within the class and provides a dynamic set and get functionality for the field we are adding. The class will not be loaded into the memory at runtime until it is invoked or get
is called onto the object.
PropertyInfo
A property is logically the same as a field. A property is a named aspect of an object’s state that can be obtained through get
and set
accessors on the object. A property may be read-only, in which case the set functionality is not supported at runtime.
What is the difference between System.Type and type?
Before we begin to look at some code, I think it Is important to be clear about what the difference between System.Type
and type
is.
According to the official documentation from Microsoft about C# types, each variable or constant in C# has a type. A type could be a string
, int
, float
, double
, etc… you get the point. Each method is capable of accepting and returning parameters and values known as type
.
You can find several categories of type
. Most common are the built-in types: string
, int
, bool
, double
, etc… you can also use value types (struct
and enums
), and reference types (classes
, interfaces
, deletages
, and records
). System.Type
is responsible for providing us with methods so that we can reflect on the objects
, variables
, and constants
– regardless of their type
. There you got the difference.
Now let’s move on to the part you have been waiting for – the code.
Working with System.Type
When we would like to retrieve the object of System.Type
, we can get it either of a type
or an object
. Below are two examples showing you how you can obtain System.Type
from both of them.
Get System.Type from an object
When you would like to get System.Type
on an object
, you can call the method GetType()
on that specific object. Below is a demo class where I call GetType()
to get the type of the object
.
namespace SystemTypeDemo;
public class Demo {
public static void Main(string[] args) {
Demo demo = new(); // Create new instance of the object
Type demoType = demo.GetType(); // Get the type of the object
Console.WriteLine(demoType); // Print it our to the console
// SystemTypeDemo.Demo
}
}
When you define a new variable of type var
, we can use System.Type
to get the type
when it is being inferred at compile time.
var x = 26;
Console.WriteLine(x.GetType());
// System.Int32
Get System.Type from a type
If we want to get the System.Type
instance of a type
, we can make use of typeof
.
Below is an example of how you can get the type of an instance.
namespace SystemTypeDemo;
public class Demo {
public static void Main(string[] args) {
Type demoType = typeof(Demo);
Console.WriteLine(demoType);
// SystemTypeDemo.Demo
}
}
What is the difference between GetType and typeof?
You are now able to use two different methods to obtain the System.Type
instance on an object and a type. But have you ever wondered what the core difference is between the two? Let’s take a deeper look at how they differentiate from each other.
The primary difference between the two are listed below for your reference.
.GetType()
is executed at run time, whiletypeof
is executed at compile time..GetType()
is only used to getSystem.Type
of an object instance, whiletypeof
returnsSystem.Type
of atype
.
When to use reflection in .NET?
Right now you are able to make use of reflection in .NET to get the System.Type
instance of an object or type. You are probably wondering – okay Christian, what can I use reflection for in my daily life? What can I achieve using reflection in my application(s)?
Earlier in this article, I listed a few different classes and methods for reflection in the roadmap section. Let’s take a look at how we can use reflection with assemblies, arguments, and attributes. These are some of the most common use cases for reflection in C#, so let’s have a look at some quick examples to give you an understanding of how you can use this in your own apps.
How to get information from assemblies?
One thing I often do when using reflection in C# is retrieve information about a particular assembly. This is done like below, where we retrieve the assembly that contains the Int32 type and displays its name and file location.
using System;
using System.Reflection;
public class Example
{
public static void Main()
{
// Get a Type object.
Type t = typeof(int);
// Instantiate an Assembly class to the assembly housing the Integer type.
Assembly assem = Assembly.GetAssembly(t);
// Display the name of the assembly.
Console.WriteLine("Name: {0}", assem.FullName);
// Get the location of the assembly using the file: protocol.
Console.WriteLine("CodeBase: {0}", assem.CodeBase);
}
}
// The example displays output like the following:
// Name: mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
// CodeBase: file:///C:/Windows/Microsoft.NET/Framework64/v4.0.30319/mscorlib.dll
Doing the above is equivalent to retrieving the value of the Type.Assembly
property and I would actually recommend using Type.Assembly
as that property is much faster in terms of performance.
In order to do the above, you must have a Type
object. This means that the assembly in which the class is defined must already be loaded in order to call the GetAssembly()
method. You could also do the following: Console.WriteLine(typeof(int).Assembly);
.
How to know an object’s actual Type?
When you want a more generic/dynamic way of returning variables without having to create huge tuples, we can use object-typed variables. However, that comes with a cost… you won’t be able to access the methods, variables, and properties.
namespace SystemTypeDemo;
public class Dog {
public Dog(string name) {
Name = name;
}
public string Name { get; }
public void Bark() {
Console.WriteLine($"Wuf wuf, I am {Name}! Can I have a treat?");
}
}
public class Demo {
public static void Main(string[] args) {
object kenya = new Dog("Kenya");
Console.WriteLine(kenya.Name);
}
}
If I ran a build for this code, I would end up with a compiler error, like this: CS1061.
CS1061: 'object' does not contain a definition for 'Name' and no accessible extension method 'name' accepting a first argument of type 'type' could be found (are you missing a using directive or an assembly reference?).
I could go the easy way and cast my object-typed variable into the type that I would like to use it with. But we got reflection – so let’s use it. We can actually retrieve the System.Type
object and interact with it to get the result we want.
public class Demo {
public static void Main(string[] args) {
object kenya = new Dog("Kenya"); // Kenya is my dog
Type kenyaType = kenya.GetType();
// Get the property and method info from the Type "kenyaType"
PropertyInfo kenyaNameProperty = kenyaType.GetProperty("Name");
MethodInfo methodInfoProperty = kenyaType.GetMethod("Bark");
// Retrieve the value or invoke the method on an instance
Console.WriteLine($"From {kenyaNameProp?.GetValue(kenya)}");
methodInfoProp?.Invoke(kenya, new object[] { });
}
}
What happens in the code above?
- First, we create a new instance of an object of a type Dog with the name Kenya.
- Then we get the property and method info and store those into a variable each. In both cases, we needed to pass a reference of the instance from which we obtained the Type (our object kenya).
- Then we print our to the console and invoke the method property.
How to get an object’s Type Arguments?
For this example, I will be creating a Dictionary
containing a string
and double
to hold the name and age of dogs.
Dictionary dogs = new() { { "Kenya", 1.5 } };
Type dogTypes = dogs.GetType();
To get the Type
arguments supplied in our dog dictionary, we can call GetGenericArguments()
to store the Type
arguments inside an array and then print each type in the console using a for each loop.
Type[] typeArguments = dogTypes.GetGenericArguments(); // Store the type arguments in an array
// print each type argument in the console
foreach (Type typeParameter in typeArguments)
{
Console.WriteLine($"Type Argument is of Type: {typeParameter}");
}
// Type Argument is of Type: System.String
// Type Argument is of Type: System.Double
Awesome! We now got a way to get the Type
arguments from a dictionary, etc… as you can see we got the two variable types printed as we expected.
How to use reflection to get data from an attribute?
If we would like to get the data stored inside an attribute by using reflection, we can call the method GetCustomAttribute()
onto the attribute. Let’s take a new example with my dog.
To get the data stored inside an attribute
, we can utilize reflection again, let’s see how we can do this on a custom attribute
named OriginCountry
.
public class OriginCountryAttribute : Attribute
{
public string originCountry;
public OriginCountryAttribute(string originCountry)
{
this.originCountry = originCountry;
}
}
Let’s use this new attribute on the Dog
class we created/used in the previous example. It looked like this:
public class Dog {
public Dog(string name) {
Name = name;
}
public string Name { get; }
public void Bark() {
Console.WriteLine($"Wuf wuf, I am {Name}! Can I have a treat?");
}
}
Now let’s use reflection in .NET to retrieve the data from our newly created attribute
named OriginCountry
, just like I have done below:
[OriginCountry("French")]
public class Dog
{
public Dog(string name) {
Name = name;
}
public string Name { get; }
Type thisType = this.GetType();
Attribute _attribute = thisType.GetCustomAttribute(
typeof(OriginCountryAttribute));
OriginCountryAttribute originCtryAttr_ = (OriginCountryAttribute)_attribute;
public void Bark() {
Console.WriteLine($"Wuf wuf, I am {Name}! Can I have a treat? I am originally from {originCtryAttr_?.originCountry}");
}
}
What happens in the code above?
- First we the
System.Type
value from ourDog
object. - Then we use reflection to get the attribute
OriginCountryAttribute
attached to theDog
class. - Finally, we ended up casting the value of the attribute type into
OriginCountryAttribute
so we are able to interact with the attribute in ourConsole.Writeline()
statement.
Please note that the method GetCustomAttribute()
only returns the type of the attribute and not the actual type passed to it.
If you were to show all attributes of an object, you could do this easily using the method GetCustomAttributes()
on a Type
. Just like I have shown below:
Type dogType = typeof(Dog);
foreach (Attribute attr in Attribute.GetCustomAttributes(dogType))
{
Console.WriteLine($"Attribute: {attr}");
}
That is how easily you can get all the attributes from a Type
.
Summary
In this article, we covered reflection in .NET and its roadmap. You should now be able to use reflection in your C# projects/applications. Reflection is not a topic many teachers are talking about. It’s a huge topic that easily could confuse a lot of especially new programmers. It took me a while to get an understanding of the reflection concept.
Reflection is really useful and can be applied in many different ways – how is totally up to you. You could try to make a demo console application and try out the C# reflection examples I have given you in this article.
If you got any questions, please let me know in the comments. Until next time – happy coding.