Reflection is a notion that allows you to inspect and retrieve type information at runtime. In other words, you may use reflection to examine the metadata of your program’s types dynamically. This article presents a deep dive into the concepts related to reflection n C# programming.
What is Programming Reflection?
Reflection can provide you with information about the assemblies that have been loaded and metadata about the types declared in them. The metadata information on a type is made accessible via the Type abstract class. You may use reflection to get metadata information about a class’s constructor, methods, fields, properties, and events, as well as the module and assembly where they are stored.
The metadata information on a type is made available via the abstract class named Type. You may use reflection to get metadata about the constructors, methods, fields, properties, and events of a class, as well as the module and assembly in which they are stored.
Although reflection in C# is like native RTTI (Runtime Type Information) of C++, there is a significant difference: reflection in C# works only with managed code and is more powerful. The reflection types in .NET are present in the System.Reflection namespace.
Hence, to work with reflection in C#, you must include the System.Reflection namespace in your program.
Read: Application Pools and Application Domains in C#.
How to Create a New Console Application in C#
To build a new Console application project with Visual Studio 2022, follow these instructions.
- Launch the Visual Studio IDE.
- Click on “Create a new project“.
- Select C# from the language dropdown list in the “Create a new project” screen.
- Select Windows as the operating system you are building your application on from the Platform drop-down list.
- Select Console from the Project Types drop-down list.
- Next, select the Console Application project template.
- Click Next.
- In the Configure your new project dialog, specify the name of your project in the Project name field.
- Choose Next.
- Select .NET 5.0 (Current) as the target framework in the Additional Information window.
- Choose Create to complete the process.
Once you follow the steps given above, the Visual Studio IDE will open your new project, with a few lines of default code in there. We will use this project in the next section to explore how we can work with reflection in C# and .Net.
Read: Visual Studio versus Visual Studio Code IDE.
Programming Reflection in C#
Once your console application project has been created, you will observe a file named Program.cs with the following code generated for you:
using System; namespace ReflectionDemo { internal class Program { static void Main(string[] args) { Console.WriteLine("Hello World!"); } } }
Before we begin coding reflection in C# to inspect an assembly, you should know how modules, types and members are related. Assembles comprise modules and modules contain types. Each type contains one or more members.
Consider the following piece of code that retrieves the type of a class at runtime and then displays metadata on the type such as, name, full name, namespace, and base type.
Type type = typeof(DateTime); Console.WriteLine("Name : {0}", type.Name); Console.WriteLine("Full Name : {0}", type.FullName); Console.WriteLine("Namespace : {0}", type.Namespace); Console.WriteLine("Base Type : {0}", type.BaseType);
When you run the above program, here’s how the output would look like:
Figure 1: Displaying metadata of the DateTime class
Now, create the following class:
class Product { public int Id { get; set; } public string Name { get; set; } public string Description { get; set; } public double Price { get; set; } }
You can use the following code snippet to retrieve the name of the class and the namespace to which it belongs:
Type type = typeof(Product); Console.WriteLine("Class: " + type.Name); Console.WriteLine("Namespace: " + type.Namespace);
Here’s the complete code listing for your reference:
using System; namespace ReflectionDemo { class Product { public int Id { get; set; } public string Name { get; set; } public string Description { get; set; } public double Price { get; set; } } internal class Program { static void Main(string[] args) { Type type = typeof(Product); Console.WriteLine("Class: " + type.Name); Console.WriteLine("Namespace: " + type.Namespace); Console.Read(); } } }
When you run the above program, here’s how the output would look like at the console window:
Figure 2: Displaying the class name and the namespace name of the Product class
You can take advantage of the GetProperties method of the Type class to retrieve the properties of a class in C#. The following code snippet illustrates how this can be achieved.
Type type = typeof(Product); PropertyInfo[] propertiesInfo = type.GetProperties(); foreach (PropertyInfo propertyInfo in propertiesInfo) { Console.WriteLine(propertyInfo.Name); }
Here’s the complete program for your reference:
using System; using System.Reflection; namespace ReflectionDemo { class Product { public int Id { get; set; } public string Name { get; set; } public string Description { get; set; } public double Price { get; set; } } internal class Program { static void Main(string[] args) { Type type = typeof(Product); PropertyInfo[] propertiesInfo = type.GetProperties(); foreach (PropertyInfo propertyInfo in propertiesInfo) { Console.WriteLine(propertyInfo.Name); } Console.Read(); } } }
When you execute the above program, the property names of all public properties of the Product class will be displayed at the console window as shown below.
Figure 3: Displaying the property names of the Product class
The Activator.CreateInstance Method
The CreateInstance method of the Activator class can be used to create an instance of a type by invoking the appropriate constructor. You can create an instance of the Product class using the Activator.CreateInstance method as shown in the code snippet given below:
Product product = (Product)Activator.CreateInstance(typeof(Product));
Assume that the product class is available not in the current assembly, but in a different assembly named Entities, which, in turn, is stored in a file called Entities.dll. You can write the following code to load the assembly from the file:
Assembly assembly = Assembly.LoadFile(@"D:\Entities.dll");
Now, retrieve the type of class Product from the assembly you just loaded in the memory using the code snippet given below:
Type type = assembly.GetType("Entities.Product");
You can now create an instance of the Product class using the Activator.CreateInstance method as shown below:
Product product = (Product)Activator.CreateInstance(type);
Retrieve Constructor Information Dynamically
The ConstructorInfo class can be used to retrieve the attributes of a constructor dynamically. You can use the GetConstructors method of the Type class to retrieve the details of constructors present in a class. Let us modify the Product class to add two constructors – one of them is the default constructor and the other is a parameterized constructor. Here’s how the updated version of the Product class would look like:
class Product { public int Id { get; set; } public string Name { get; set; } public string Description { get; set; } public double Price { get; set; } private double Discount { get; set; } public Product() { //This is the default constructor. } public Product(int id, string name, string description, double price, double discount) { Id = id; Name = name; Description = description; Price = price; Discount = discount; } }
The following code snippet shows how you can retrieve information about the constructors of the Product class:
Type type = typeof(Product); ConstructorInfo[] constructorInfo = type.GetConstructors(); foreach (ConstructorInfo c in constructorInfo) { Console.WriteLine(c.ToString()); }
Read: How to Access Interop Objects in C#.
Retrieve Method Information Dynamically
You can use the MethodInfo class to retrieve information about the methods of a class. Let us add a simple method named Display to our Product class. The updated Product class is below.
class Product { public int Id { get; set; } public string Name { get; set; } public string Description { get; set; } public double Price { get; set; } private double Discount { get; set; } public Product() { //This is the default constructor. } public Product(int id, string name, string description, double price, double discount) { Id = id; Name = name; Description = description; Price = price; Discount = discount; } public void Display() { Console.WriteLine("Product Id: {0}, Price: {1}", Id, Price); } }
You can write the following piece of code to retrieve method names of the Product class at runtime.
Type type = typeof(Product); MethodInfo[] methodInfos = type.GetMethods(); foreach (MethodInfo methodInfo in methodInfos) { Console.WriteLine(methodInfo.Name); }
When you execute the above piece of code, you will see the method names of the Product class displayed at the console window as shown below:
Figure 4: Displaying the methods of the Product class
Note that, apart from the Display method, you will see some additional methods as well. The GetType, ToString, Equals, and GetHashCode methods are inherited from the base class, i.e., the Object class. The other methods are implicit methods that are created to read/write values from and to the properties of the Product class.
.Net, C#, and Reflection Tutorial
Reflection refers to the ability of your managed code to read its metadata to identify assemblies, modules, and type information during runtime. Reflection offers objects that encapsulate assemblies, modules, and types. A program reflects on itself by extracting information from its assembly and uses that metadata to either inform or change its behavior. This article provided an overview of reflection, its benefits and how we can work with it in C# and ,Net.