C# Reflection Tutorial For Beginners With Examples

Reflection is a technique using which a program can be modified during runtime. Using reflection a program's behavior can be manipulated dynamically and a program can be made more adaptable.
To work with reflection the required classes are provided in System.Reflection namespace.

Features of C# Reflection

Important features of Reflection are mentioned below.
  • Accessing Metadata - Ability to access metadata associated with an assembly, class and its members.
  • Discovering Types - Allows to discover the types available in an Assembly.
  • Late Binding - Can be used to get or set properties, invoke methods, access fields of an object dynamically instantiated during runtime. This is also know as dynamic invocation.
  • Reflection Emit- Can be used to dynamically create types during runtime.
In this tutorial we will discuss only the first three features as the fourth feature is very rarely used in programming world.

Accessing Metadata

Reflection can be used to access the metadata associated with a class.
Metadata is additional information added to a class, assembly or class member. This information is added using Attributes.
The MemberInfo class available in the System.Reflection namespace is used to achieve this.

Example to Access Metadata

In the following example a Weather class is created with the attribute Serializable. The GetCustomAttributes method retrieves all the attributes associated with the Weather class. These attributes are then read using a loop.
       
  
    using System;
    using System.Reflection;
    using System.Diagnostics;

    [Serializable]
    [DebuggerDisplay("Weather Class")]
    public class Weather
    {
        private string weatherData = "Initialize from web service";

        public string ReadWeather()
        {
            return weatherData;
        }
    }

    class Program
    {
        public static void Main()
        {
            MemberInfo memberInfo = typeof(Weather);
            object[] attributes = memberInfo.GetCustomAttributes(false);

            foreach (var metadata in attributes)
            {
                Console.WriteLine(metadata);
            }
            Console.ReadKey();
        }
    }
        
Output:
System.Diagnostics.DebuggerDisplayAttribute
System.SerializableAttribute

Discovering Types

This is another feature of C# Reflection that helps to discover the types associated with an assembly. Using this feature the members of a type, signature of the type's methods, the interfaces supported by the type and the type's base class can be determined.
To achieve this the static method Assembly.LoadFrom is used.

Example of Type Discovery

To explain how this works first a C# class library project named as Utility is created. To do this in Visual Studio 2010 its File > New > Project > Class Library (Select Visual C# on the left pane). Although an assembly can be created in any language for this example we have used Visual C#.
    using System;
    using System.Diagnostics;

    namespace Utility
    {
        public interface ILogger
        {
            void Log(string message);
        }

        [Serializable]
        [DebuggerDisplay("Weather Class")]
        public class Weather : ILogger
        {
            private string weatherData = "Initialize from web service";

            public string ReadWeather()
            {
                return weatherData;
            }

            public void Log(string message)
            {
                //Code to Log data
            }
        }

        public class Encrypt : ILogger
        {
            public void EncryptString(string data)
            {
                //Code to encrypt a string
            }

            public void Log(string message)
            {
                //Code to Log data
            }
        }
    }
        
When the above code was compiled an assembly called as "Utiltiy.dll" was created. This assembly was then copied to 'Projects' folder in the 'C:' drive.
In the below code the above assembly was loaded using the Assembly.LoadFrom method and the types and interface used in the Utility.dll are displayed in the console window.
    using System;
    using System.Reflection;
    using System.Diagnostics;

    class Program
    {
        public static void Main()
        {
          Assembly weather = Assembly.LoadFrom(@"C:\Projects\Utility.dll");
          Type[] types = weather.GetTypes();

            foreach(var weatherType in types)
            {
                Console.WriteLine(weatherType);
            }
            Console.ReadKey();
        }
    }
        
Output:
Utility.ILogger
Utility.Weather
Utility.Encrypt
The above example can be modified to show all the members inside a type. This is achieved by using GetMembers method.
    using System;
    using System.Reflection;
    using System.Diagnostics;

    class Program
    {
        public static void Main()
        {
          Assembly weather =  Assembly.LoadFrom(@"C:\Projects\Utility.dll");
          Type[] types = weather.GetTypes();

            foreach(var weatherType in types)
            {
                Console.WriteLine("The type is {0} :-", weatherType);
         
                MemberInfo[] memberinfoArray = weatherType.GetMembers();
                foreach (var info in memberinfoArray)
                {
                    Console.WriteLine("{0} is a {1}",info,info.MemberType);
                }
                Console.WriteLine("----------------------------");
            }
            Console.ReadKey();
        }
    }
        
Output:
The type is Utility.ILogger :-
Void Log(System.String) is a Method
----------------------------
The type is Utility.Weather :-
System.String ReadWeather() is a Method
Void Log(System.String) is a Method
System.String ToString() is a Method
Boolean Equals(System.Object) is a Method
Int32 GetHashCode() is a Method
System.Type GetType() is a Method
Void .ctor() is a Constructor
----------------------------
The type is Utility.Encrypt :-
Void EncryptString(System.String) is a Method
Void Log(System.String) is a Method
System.String ToString() is a Method
Boolean Equals(System.Object) is a Method
Int32 GetHashCode() is a Method
System.Type GetType() is a Method
Void .ctor() is a Constructor
----------------------------

Late Binding Using Reflection

Once a method has been discovered then it can be invoked using reflection. This process is called as late binding using Reflection. This is Late Binding because the binding with the method is done in runtime.
When a normal C# program is written where a object is created and its methods invoked then the compiler knows beforehand that the method is going to be invoked during program's execution. So, the binding happens before the actual program is run. This process is termed as early binding. When reflection is used this process is delayed till the execution of the program and hence it is late binding.
To achieve late binding using reflection the .NET Type and Activator class is used.

Example of Late Binding Using Reflection

To demonstrate late binding in our example, a Sum method is created inside a custom Utilityclass. Our example will invoke the Sum method using reflection. The code to do this is written inside the Main method.
In our example, first a type object is created that contains the type of the Util.Utility class. Then using the Activator.CreateInstance method an instance of the Util.Utility class was created.
         
    Type utilityType = Type.GetType("Util.Utility");
    Object utilityInstance = Activator.CreateInstance(utilityType);
        
Similarly, a type array to hold the type of the parameters and another array to hold the values of the parameters was created. The values passed here are 1 and 5.
        
    Type[] paramTypeArray = new Type[2];
    paramTypeArray[0] = Type.GetType("System.Int32");
    paramTypeArray[1] = Type.GetType("System.Int32");
        
    Object[] paramValueArray = new Object[2];
    paramValueArray[0] = 1;
    paramValueArray[1] = 5;
        
The MethodInfo object named as utilityType invokes the actual Sum method. The GetMethod which accepts the method name and the type of the parameters as an array is used to create the MethodInfo object.
    MethodInfo methodSum = utilityType.GetMethod("Sum", paramTypeArray);
            
The instance of the class whose method has to be invoked should be passed as a parameter to the invoke method of the MethodInfo object. The values that should be passed to the Summethod should be passed as an array of objects to the invoke method.
        
    Object result = methodSum.Invoke(utilityInstance, paramValueArray);
        
Invoking a method like this is also called as Dynamic Invocation. The complete code is shown in the below example.
    using System;
    using System.Reflection;

    namespace Util
    {
        public class Utility
        {
            public static int Sum(int value1, int value2)
            {
                return value1 + value2;
            }
        }
    }

    class Program
    {
        public static void Main()
        {
            //Create the Utility type
            Type utilityType = Type.GetType("Util.Utility");
        
            //Create the Utility class instance from the file type
            Object utilityInstance = Activator.CreateInstance(utilityType);

            //A parameter type array is created to hold the type of the parameter
            Type[] paramTypeArray = new Type[2];
            paramTypeArray[0] = Type.GetType("System.Int32");
            paramTypeArray[1] = Type.GetType("System.Int32");
                
            MethodInfo methodSum = utilityType.GetMethod("Sum", paramTypeArray);

            //Holds the value of the parameters
            Object[] paramValueArray = new Object[2];
            paramValueArray[0] = 1;
            paramValueArray[1] = 5;

            Object result = methodSum.Invoke(utilityInstance, paramValueArray);
            Console.WriteLine(result);
       
            Console.ReadKey();
        }
    }
        
Output:
6