Have you ever wondered how we can call a method using only its name or create an instance of an object dynamically? Well, reflection have you covered. So, what is the reflection you might ask?

Reflection is the feature present in C# that can read the metadata of the code present in the same system.

The use of reflection can be seen in IDE where we can inspect code to find its properties, methods, etc. Reflection can also be used for late binding, i.e we can dynamically create the instance of the object and call its method. Let's see how we can do all those things in code.

    public class Person
    {
        private string name;

        public void SetName(string name)
        {
            this.name = name;
            Console.WriteLine($"my name is {name}");
        }
        public void BuyFruit(Fruit fruit)
        {
            Console.WriteLine($"{name} bought {fruit}");
        }
    }
    public class Fruit
    {
        public string name;
        public int price;

        public void Initialize(string name, int price)
        {
            this.name = name;
            this.price = price;
        }
        

        public override string ToString()
        {
            return $"{name} with  price  {price}";
        }
    }
    
    public class Program
    {
        public static void Main(string[] args)
        {
            Person person = new Person();
            person.SetName("Tom");
            Fruit apple = new Fruit();
            apple.Initialize("apple", 10);
            person.BuyFruit(apple);
        }
}
Code #1: The normal method of calling function

In the above code, I have created two classes Person and Fruit and created their instance. Running the above code will print:

my name is Tom
Tom bought apple with price 10
Output #1

This is the normal way of creating instances and calling their methods. So how can we use reflection to dynamically create an instance and call its function?

using System;
using System.Reflection;

namespace Reflection
{
    public class Program
    {
        public static void Main(string[] args)
        { 
            DynamicallyCallMethod();
        }
        
        private static void DynamicallyCallMethod()
        {
            Assembly assembly = Assembly.GetExecutingAssembly();
            
            Type PersonType = assembly.GetType("Reflection.Person"); // create the type dynamically using its full name in assembly.

            object personObject = Activator.CreateInstance(PersonType); // create instance of the person object
            
            personObject.GetType().GetMethod("SetName").Invoke(personObject, new []{"Jerry"});
            
            
            Type FruitType = assembly.GetType("Reflection.Fruit"); // create the type dynamically using string

            object fruitObject = Activator.CreateInstance(FruitType); // create instance of the fruit object
            
            fruitObject.GetType().GetMethod("Initialize").Invoke(fruitObject, new object[] {"Orange" , 20});
            
            string Info =(string) fruitObject.GetType().GetMethod("ToString").Invoke(fruitObject, new object[] {});
            
            personObject.GetType().GetMethod("BuyFruit").Invoke(personObject, new []{fruitObject});
        }

    }

   
}
Code #2

When we look at example 2 we see that compiler doesn't throw an error even if there is no class named Person and Fruit and compiles just fine. But running this will throw an error because the type of object it wants to create in runtime doesn't exist. So after adding the class Person and Fruit, the above code will output

my name is Jerry
Jerry bought Orange with price 20

Now let's see what is happening in the above code:

  1. First, we are getting the current assembly that holds metadata of the current class.
  2. We create a PersonType by using the name of the Person class. Note we are using Reflection.Person to get the type from the assembly because the Person resides inside the namespace Reflection.
  3. Next, we're creating an instance of the person using the type we got from reflecting the current assembly.
  4. And finally, we are getting methodInfo of the class type Person dynamically invoking it.

Creating Fruit type object is similar to creating Person type object. It creates instances of Person type and Fruit type and invoke their methods.

Reflection is a very powerful feature that lets us create interesting features. An example can be creating a network call where you pass the object type and its function so that other clients can dynamically create the instance of that type and invoke its method.

And that's it, I hope you learned something new from this article. Go for an in-depth study of Reflection and explore various topics not covered here.