CodeGym /Courses /C# SELF /Reflection: System.Reflect...

Reflection: System.Reflection

C# SELF
Level 63 , Lesson 0
Available

1. Introduction

Reflection is the ability of your program to inspect its own structure at runtime. Imagine your program asking itself: 'What methods do I have? What properties does this type have?' and getting an answer at runtime.

Real-world use cases for reflection

  • Building plugins and modules: You don't need to know the class ahead of time — just load a DLL and discover its methods on the fly.
  • Automated testing: Test frameworks like xUnit and NUnit find test methods via reflection.
  • Serialization: With reflection you can automatically turn any object into JSON/XML without manually iterating properties.
  • Validating data by attributes: For example, you added a [Required] attribute to a property — and you want to automatically validate all such properties.

Why this matters in interviews

Reflection skills are often asked about in interviews because they show your understanding of how .NET works and your ability to solve dynamic problems. If you want to write your own frameworks or extensions, reflection is indispensable.

2. Namespace and basic reflection classes

All reflection functionality in C# is available via the System.Reflection namespace. Don't forget at the top of your file:

using System.Reflection;

Main reflection types

Class/Type Description
Type
Represents a type description in .NET (for example, class, interface, enum, etc.)
PropertyInfo
A type's property
MethodInfo
A type's method
FieldInfo
A type's field
ConstructorInfo
A type's constructor
Assembly
Represents an assembly (EXE or DLL)

Key methods on Type

Method Description
GetProperties()
Returns all public properties
GetFields()
Returns all public fields
GetMethods()
Returns all public methods
GetConstructors()
Returns all public constructors
GetInterfaces()
Returns all interfaces implemented by the type
GetCustomAttributes()
Returns all attributes applied to the type

You can often pass BindingFlags to these methods to access private or static members, but for now we'll stick to the basic scenario.

3. The very first step: getting a Type object

All reflection techniques are built around working with a Type object.

There are several ways to get that object, let's look at each:

Way 1: From an object

string text = "Hello, Reflection!";
Type type1 = text.GetType();
Console.WriteLine(type1.Name); // String

Way 2: From the type name

Type type2 = typeof(int);
Console.WriteLine(type2.FullName); // System.Int32

Way 3: By string (dynamically)

Type type3 = Type.GetType("System.Double");
Console.WriteLine(type3); // System.Double

Be careful! For custom classes from other assemblies you need to provide the full type name including the assembly.

4. Exploring a type's structure: properties, methods, fields and constructors

Here comes the moment of truth: we have a Type object — now we can learn anything we want.

Getting the list of properties

Type type = typeof(DateTime);
PropertyInfo[] properties = type.GetProperties();

foreach (var prop in properties)
{
    Console.WriteLine($"{prop.PropertyType.Name} {prop.Name}");
}

Output:

Int32 Day
Int32 Month
Int32 Year
DayOfWeek DayOfWeek
...

Getting the list of methods

MethodInfo[] methods = type.GetMethods();

foreach (var method in methods)
{
    Console.WriteLine($"{method.ReturnType.Name} {method.Name}()");
}

Big types can have lots of methods! Often you need not all of them but only "your own" ones (for example, excluding inherited methods from object). We'll cover that later.

Getting the list of fields

FieldInfo[] fields = type.GetFields();

foreach (var field in fields)
{
    Console.WriteLine($"{field.FieldType.Name} {field.Name}");
}

But most normal classes don't have public fields, because encapsulation is everything :)

Getting constructors

ConstructorInfo[] constructors = type.GetConstructors();

foreach (var ctor in constructors)
{
    Console.WriteLine($"Constructor: {ctor}");
}

5. Applying reflection in practice: inspecting our app

Recall that in our learning project there's a class TaskItem where we store tasks. Let's inspect it via reflection.

public class TaskItem
{
    public int Id { get; set; }
    public string Title { get; set; }
    public DateTime DueDate { get; set; }
    public bool IsCompleted;
}

Here's how to see the whole "insides" of our type at runtime:

Type taskType = typeof(TaskItem);

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

Console.WriteLine("Fields:");
foreach (var field in taskType.GetFields())
    Console.WriteLine($"  {field.FieldType.Name} {field.Name}");

Console.WriteLine("Methods:");
foreach (var method in taskType.GetMethods())
    Console.WriteLine($"  {method.ReturnType.Name} {method.Name}()");

Try adding new properties to the class and see how reflection picks them up. That's how general serializers and object inspectors work.

6. Visualization: what a Type object looks like (diagram)


+-------------------------+
|         Type            |
+-------------------------+
|  .Name                  |
|  .FullName              |
|  .Namespace             |
|  .Assembly              |
+-------------------------+
|  .GetProperties()       |
|  .GetFields()           |
|  .GetMethods()          |
|  .GetConstructors()     |
|  .GetInterfaces()       |
|  .GetCustomAttributes() |
+-------------------------+

This is our "type portrait" — it has a name, assembly, a set of members and attributes.

Getting basic info about a type

A Type object can tell you what kind of type it is:

Type t = typeof(double);

Console.WriteLine(t.Name);         // Double
Console.WriteLine(t.FullName);     // System.Double
Console.WriteLine(t.Namespace);    // System
Console.WriteLine(t.IsClass);      // False
Console.WriteLine(t.IsValueType);  // True
Console.WriteLine(t.IsEnum);       // False
Console.WriteLine(t.IsPrimitive);  // True

7. Finding a specific property, method or field by name

Property

var prop = taskType.GetProperty("Title");
if (prop != null)
{
    Console.WriteLine($"Property 'Title' type: {prop.PropertyType}");
}

Method

var method = taskType.GetMethod("ToString");
if (method != null)
{
    Console.WriteLine($"Method 'ToString' returns: {method.ReturnType}");
}

Field

var field = taskType.GetField("IsCompleted");
if (field != null)
{
    Console.WriteLine($"Field 'IsCompleted' type: {field.FieldType}");
}

8. Using reflection for dynamic value access

Now we don't just look — we also touch values!

Reading a property value

TaskItem item = new TaskItem { Id = 1, Title = "Test reflection", DueDate = DateTime.Today };
PropertyInfo titleProp = item.GetType().GetProperty("Title");

if (titleProp != null)
{
    object value = titleProp.GetValue(item);
    Console.WriteLine($"Title: {value}");
}

Setting a property value

titleProp.SetValue(item, "Modified title");
Console.WriteLine(item.Title);

Working with fields

FieldInfo completedField = item.GetType().GetField("IsCompleted");
completedField.SetValue(item, true);
Console.WriteLine(item.IsCompleted); // true

For private fields and properties you need special flags, for example BindingFlags.NonPublic. We'll cover that later.

9. Invoking methods via reflection

You can invoke methods even if you didn't know them at compile time!

TaskItem task = new TaskItem { Title = "Reflection is cool!" };
MethodInfo method = task.GetType().GetMethod("ToString");

if (method != null)
{
    object result = method.Invoke(task, null);
    Console.WriteLine(result);
}

If a method has parameters, pass them as an object array:

public class Calculator
{
    public int Add(int a, int b) => a + b;
}

var calc = new Calculator();
var addMethod = typeof(Calculator).GetMethod("Add");
object[] parameters = { 5, 3 };
object sumResult = addMethod.Invoke(calc, parameters);
Console.WriteLine(sumResult); // 8

10. Dynamically creating objects via constructor

Reflection lets you create objects without the new operator!

Type taskType = typeof(TaskItem);
object newTask = Activator.CreateInstance(taskType);
// It's the same TaskItem (but typed as object)
Console.WriteLine(newTask.GetType().Name); // TaskItem

You can pass constructor parameters:

public class User
{
    public string Name { get; }
    public User(string name) { Name = name; }
}

Type userType = typeof(User);
object user = Activator.CreateInstance(userType, "Alice");
Console.WriteLine(((User)user).Name); // Alice

11. Common mistakes when working with System.Reflection

Mistake #1: Accessing private members without BindingFlags.
By default reflection sees only public members. Without BindingFlags.NonPublic you can't access private fields or methods.

Mistake #2: Ignoring null checks.
Methods like GetProperty, GetMethod and others can return null if the member isn't found. Without checking this you'll get a NullReferenceException.

Mistake #3: Picking the wrong method overload.
When there are multiple overloads, GetMethod might return the wrong one. Use GetMethod with parameter types specified.

Mistake #4: Using reflection in performance-critical code.
Reflection is slower than direct calls. For high-performance code use metadata caching or delegates.

2
Task
C# SELF, level 63, lesson 0
Locked
Getting a list of properties, methods, and fields of a type
Getting a list of properties, methods, and fields of a type
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION