C# - Reflection: A Beginner's Guide

Hello there, future coding superstar! Today, we're going to embark on an exciting journey into the world of C# Reflection. Don't worry if you've never written a line of code before – I'll be your friendly guide, and we'll explore this topic together, step by step. By the end of this tutorial, you'll have a solid understanding of what Reflection is and how to use it. So, let's dive in!

C# - Reflection

What is Reflection?

Imagine you're at a fancy restaurant, and you're handed a menu. But this isn't just any menu – it's magical! Not only can you see the dishes, but you can also peek into the kitchen to see how they're made, what ingredients are used, and even modify the recipe on the fly. That's essentially what Reflection does in C#, but with code instead of food.

In technical terms, Reflection is a feature in C# that allows a program to inspect, interact with, and modify its own structure and behavior at runtime. It's like giving your program a mirror to look at itself!

Applications of Reflection

Now, you might be wondering, "Why would I want my program to look at itself?" Great question! Let's explore some practical applications of Reflection:

  1. Dynamic loading of assemblies: Reflection allows you to load and use assemblies (think of these as packages of code) that weren't necessarily known when you wrote your program.

  2. Creating instances of types: You can create objects of a certain type without knowing the exact type at compile-time.

  3. Invoking methods: Reflection enables you to call methods on objects dynamically.

  4. Accessing and modifying fields and properties: You can read and write to fields and properties of an object.

  5. Attribute inspection: You can examine the attributes (additional information) attached to various program elements.

Let's look at these in more detail with some code examples!

Example 1: Creating an instance dynamically

using System;
using System.Reflection;

class Program
{
    static void Main()
    {
        // Get the type of the string class
        Type stringType = typeof(string);

        // Create an instance of string using Reflection
        object str = Activator.CreateInstance(stringType, new object[] { "Hello, Reflection!" });

        Console.WriteLine(str); // Output: Hello, Reflection!
    }
}

In this example, we're using Reflection to create an instance of the string class. It's like telling C#, "I want to create something, but I'll tell you what it is at runtime." This is particularly useful when you don't know the exact type you need to create until your program is running.

Example 2: Invoking a method dynamically

using System;
using System.Reflection;

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

class Program
{
    static void Main()
    {
        // Create an instance of MyClass
        MyClass obj = new MyClass();

        // Get the type of MyClass
        Type type = obj.GetType();

        // Get the MethodInfo for the SayHello method
        MethodInfo methodInfo = type.GetMethod("SayHello");

        // Invoke the method
        methodInfo.Invoke(obj, new object[] { "Reflection" });
        // Output: Hello, Reflection!
    }
}

Here, we're using Reflection to call the SayHello method on our MyClass object. It's like we're telling C#, "I know this object has a method called 'SayHello', please call it for me." This is super handy when you need to call methods on objects where you don't know the exact type at compile-time.

Viewing Metadata

One of the coolest things about Reflection is that it lets us peek under the hood of our code. We can view metadata about types, methods, properties, and more. Let's take a look!

Example 3: Viewing type metadata

using System;
using System.Reflection;

class Program
{
    static void Main()
    {
        Type stringType = typeof(string);

        Console.WriteLine($"Type Name: {stringType.Name}");
        Console.WriteLine($"Full Name: {stringType.FullName}");
        Console.WriteLine($"Namespace: {stringType.Namespace}");
        Console.WriteLine($"Is it a class? {stringType.IsClass}");
        Console.WriteLine($"Base Type: {stringType.BaseType}");

        Console.WriteLine("\nMethods:");
        foreach (MethodInfo method in stringType.GetMethods())
        {
            Console.WriteLine(method.Name);
        }
    }
}

This code is like asking the string class to tell us all about itself. We're finding out its name, what namespace it lives in, whether it's a class (spoiler: it is!), what it inherits from, and even listing all its methods. It's like having a conversation with your code!

A Comprehensive Example

Let's put it all together with a more complex example that showcases various aspects of Reflection:

using System;
using System.Reflection;

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }

    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }

    public void Introduce()
    {
        Console.WriteLine($"Hi, I'm {Name} and I'm {Age} years old.");
    }
}

class Program
{
    static void Main()
    {
        // Create a Person object using Reflection
        Type personType = typeof(Person);
        object[] constructorArgs = { "Alice", 30 };
        Person person = (Person)Activator.CreateInstance(personType, constructorArgs);

        // Get and set property values using Reflection
        PropertyInfo nameProperty = personType.GetProperty("Name");
        PropertyInfo ageProperty = personType.GetProperty("Age");

        Console.WriteLine($"Current Name: {nameProperty.GetValue(person)}");
        nameProperty.SetValue(person, "Bob");
        ageProperty.SetValue(person, 25);

        // Invoke a method using Reflection
        MethodInfo introduceMethod = personType.GetMethod("Introduce");
        introduceMethod.Invoke(person, null);

        // Display all methods of the Person class
        Console.WriteLine("\nMethods of Person class:");
        foreach (MethodInfo method in personType.GetMethods())
        {
            Console.WriteLine(method.Name);
        }
    }
}

This example demonstrates creating an object, getting and setting property values, invoking a method, and listing all methods of a class, all using Reflection. It's like we're puppeteers, controlling our Person object entirely through Reflection!

Reflection Methods Table

Here's a handy table of some common Reflection methods:

Method Description
Type.GetType() Gets the Type with the specified name
Object.GetType() Gets the Type of the current instance
Type.GetMethods() Returns all the public methods of the current Type
Type.GetProperties() Returns all the public properties of the current Type
Type.GetFields() Returns all the public fields of the current Type
Type.GetConstructors() Returns all the public constructors of the current Type
Activator.CreateInstance() Creates an instance of a type
MethodInfo.Invoke() Invokes a method
PropertyInfo.GetValue() Gets the value of a property
PropertyInfo.SetValue() Sets the value of a property

Conclusion

Whew! We've covered a lot of ground today. Reflection might seem a bit mind-bending at first, but it's an incredibly powerful tool in your C# toolkit. It allows your programs to be more flexible and dynamic, adapting to conditions at runtime.

Remember, with great power comes great responsibility. Reflection can be slower than direct code and can potentially break type safety if not used carefully. But when used judiciously, it can solve problems that would be difficult or impossible to tackle otherwise.

Keep practicing, keep exploring, and before you know it, you'll be reflecting like a pro! Happy coding, future C# maestro!

Credits: Image by storyset