C# - Reflection



Reflection objects are used for obtaining type information at runtime. The classes that give access to the metadata of a running program are in the System.Reflection namespace.

The System.Reflection namespace contains classes that allow you to obtain information about the application and to dynamically add types, values, and objects to the application.

Applications of Reflection

Reflection has the following applications −

  • It allows view attribute information at runtime.

  • It allows examining various types in an assembly and instantiate these types.

  • It allows late binding to methods and properties

  • It allows creating new types at runtime and then performs some tasks using those types.

Namespace for Reflection

To use reflection in your C# code, you need to include the System.Reflection namespace. Here is the syntax to include it:

using System.Reflection;

Viewing Metadata

We have mentioned in the preceding chapter that using reflection you can view the attribute information.

The MemberInfo object of the System.Reflection class needs to be initialized for discovering the attributes associated with a class. To do this, you define an object of the target class, as −

System.Reflection.MemberInfo info = typeof(MyClass);

Example 1: Creating and Using Custom Attributes

This C# example, demonstrates how to define and apply a custom attribute using the System.Attribute class −

using System;

// Define the custom attribute
[AttributeUsage(AttributeTargets.All)]
public class HelpAttribute : System.Attribute
{
    public readonly string Url;

    private string topic;
    public string Topic   // Topic is a named parameter
    {
        get { return topic; }
        set { topic = value; }
    }

    public HelpAttribute(string url)   // url is a positional parameter
    {
        this.Url = url;
    }
}

// Apply the attribute to a class
[Help("Information on the class MyClass", Topic = "Class Info")]
class MyClass
{
}

namespace AttributeAppl
{
    class Program
    {
        static void Main(string[] args)
        {
            System.Reflection.MemberInfo info = typeof(MyClass);
            object[] attributes = info.GetCustomAttributes(true);

            foreach (object attr in attributes)
            {
                HelpAttribute helpAttr = attr as HelpAttribute;
                if (helpAttr != null)
                {
                    Console.WriteLine("Help URL: " + helpAttr.Url);
                    Console.WriteLine("Topic: " + helpAttr.Topic);
                }
            }

            Console.ReadKey();
        }
    }
}

When it is compiled and run, it displays the name of the custom attributes attached to the class MyClass

Help URL: Information on the class MyClass
Topic: Class Info

Example 2: Using The DeBugInfo

In this example, we use the DeBugInfo attribute created in the previous chapter and use reflection to read metadata in the Rectangle class.

using System;
using System.Reflection;
namespace BugFixApplication {
   //a custom attribute BugFix to be assigned to a class and its members
   [AttributeUsage(
      AttributeTargets.Class |
      AttributeTargets.Constructor |
      AttributeTargets.Field |
      AttributeTargets.Method |
      AttributeTargets.Property,
      AllowMultiple = true)]

   public class DeBugInfo : System.Attribute {
      private int bugNo;
      private string developer;
      private string lastReview;
      public string message;
      
      public DeBugInfo(int bg, string dev, string d) {
         this.bugNo = bg;
         this.developer = dev;
         this.lastReview = d;
      }
      public int BugNo {
         get {
            return bugNo;
         }
      }
      public string Developer {
         get {
            return developer;
         }
      }
      public string LastReview {
         get {
            return lastReview;
         }
      }
      public string Message {
         get {
            return message;
         }
         set {
            message = value;
         }
      }
   }
   [DeBugInfo(45, "Zara Ali", "12/8/2012", Message = "Return type mismatch")]
   [DeBugInfo(49, "Nuha Ali", "10/10/2012", Message = "Unused variable")]
   
   class Rectangle {
      //member variables
      protected double length;
      protected double width;
      
      public Rectangle(double l, double w) {
         length = l;
         width = w;
      }
      [DeBugInfo(55, "Zara Ali", "19/10/2012", Message = "Return type mismatch")]
      public double GetArea() {
         return length * width;
      }
      [DeBugInfo(56, "Zara Ali", "19/10/2012")]
      public void Display() {
         Console.WriteLine("Length: {0}", length);
         Console.WriteLine("Width: {0}", width);
         Console.WriteLine("Area: {0}", GetArea());
      }
   }//end class Rectangle
   
   class ExecuteRectangle {
      static void Main(string[] args) {
         Rectangle r = new Rectangle(4.5, 7.5);
         r.Display();
         Type type = typeof(Rectangle);
         
         //iterating through the attribtues of the Rectangle class
         foreach (Object attributes in type.GetCustomAttributes(false)) {
            DeBugInfo dbi = (DeBugInfo)attributes;
            
            if (null != dbi) {
               Console.WriteLine("Bug no: {0}", dbi.BugNo);
               Console.WriteLine("Developer: {0}", dbi.Developer);
               Console.WriteLine("Last Reviewed: {0}", dbi.LastReview);
               Console.WriteLine("Remarks: {0}", dbi.Message);
            }
         }
         
         //iterating through the method attribtues
         foreach (MethodInfo m in type.GetMethods()) {
            
            foreach (Attribute a in m.GetCustomAttributes(true)) {
               DeBugInfo dbi = (DeBugInfo)a;
               
               if (null != dbi) {
                  Console.WriteLine("Bug no: {0}, for Method: {1}", dbi.BugNo, m.Name);
                  Console.WriteLine("Developer: {0}", dbi.Developer);
                  Console.WriteLine("Last Reviewed: {0}", dbi.LastReview);
                  Console.WriteLine("Remarks: {0}", dbi.Message);
               }
            }
         }
         Console.ReadLine();
      }
   }
}

When the above code is compiled and executed, it produces the following result −

Length: 4.5
Width: 7.5
Area: 33.75
Bug No: 49
Developer: Nuha Ali
Last Reviewed: 10/10/2012
Remarks: Unused variable
Bug No: 45
Developer: Zara Ali
Last Reviewed: 12/8/2012
Remarks: Return type mismatch
Bug No: 55, for Method: GetArea
Developer: Zara Ali
Last Reviewed: 19/10/2012
Remarks: Return type mismatch
Bug No: 56, for Method: Display
Developer: Zara Ali
Last Reviewed: 19/10/2012
Remarks: 

Get Type Info Using Reflection

You can retrieve type metadata using the typeof keyword or the GetType() method.

Example

The following example demonstrates how to get the class name and namespace using reflection:

using System;
using System.Reflection;

class Person {
    public int Id { get; set; }
    public void Greet() {
        Console.WriteLine("Namaste from Greet!");
    }
}

class Program {
    static void Main() {
        Type type = typeof(Person);

        Console.WriteLine("Class Name: " + type.Name);
        Console.WriteLine("Namespace: " + type.Namespace);
    }
}

When the above code is compiled and executed, it produces the following result −

Class Name: Person
Namespace:

Get Properties, Methods, and Fields

You can also get the class members using reflection:

Example

The following example demonstrates how to list properties, methods, and fields of a class:

using System;
using System.Reflection;

class Student {
    public string Name { get; set; } = "Aarav";
    public int RollNo { get; set; } = 101;
    public string School = "Delhi Public School";

    public void Introduce() {
        Console.WriteLine($"Hi, I am {Name}, Roll No: {RollNo}.");
    }
}

class Program {
    static void Main() {
        Type type = typeof(Student);

        Console.WriteLine("Properties:");
        foreach (var prop in type.GetProperties()) {
            Console.WriteLine(prop.Name);
        }

        Console.WriteLine("Methods:");
        foreach (var method in type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)) {
            Console.WriteLine(method.Name);
        }

        Console.WriteLine("Fields:");
        foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)) {
            Console.WriteLine(field.Name);
        }
    }
}

When the above code is compiled and executed, it produces the following result −

Properties:
Name
RollNo
Methods:
Introduce
Fields:
School

Invoke Method Dynamically

You can even invoke methods at runtime by using the reflection.

Example

The following example demonstrates how to invoke a method dynamically using reflection:

using System;
using System.Reflection;

class Greeter {
    public void SayHello(string name) {
        Console.WriteLine($"Hello, {name}!");
    }
}

class Program {
    static void Main() {
        Type type = typeof(Greeter);
        object obj = Activator.CreateInstance(type);

        MethodInfo method = type.GetMethod("SayHello");
        method.Invoke(obj, new object[] { "Ravi" });
    }
}

When the above code is compiled and executed, it produces the following result −

Hello, Ravi!

Get Assembly Info

You can also retrieve information about assemblies using the reflection.

Example

The following example demonstrates how to get the current assembly name and its defined types:

using System;
using System.Reflection;

class Program {
    static void Main() {
        Assembly assembly = Assembly.GetExecutingAssembly();
        Console.WriteLine("Assembly Full Name: " + assembly.FullName);

        Console.WriteLine("Defined Types:");
        foreach (var t in assembly.DefinedTypes) {
            Console.WriteLine(t.Name);
        }
    }
}

When the above code is compiled and executed, it produces the following result −

Assembly Full Name: main, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
Defined Types:
Program
Advertisements