What is Reflection?
- C# program compiles into an assembly that includes metadata, compiled code and resources. Inpsecting the metadata and compiled code at runtime is called reflection
To understand reflection properly , you need to understand what metadata is first.
What is metadata?
- In C# , metadata refers to information about built-in or custom types, members and other constructs defined in an assembly.
- In other words, metadata is not the data itself — it’s data about data.
- It tells the runtime things like : What classes and interfaces exist, Which field, properties and methods they have, what attributes decorate them and more.
How It Works?
- Let’s take a look at how to obtain a Type — the backbone of the Reflection API — inspect its metadata, and use it to interact with your code at runtime.
In C#, every object and type has metadata attached to it. The System.Type class represents this information. You can get a Type object in a few ways:
-
Using typeof(T)
Type type1 = typeof(Player)
-
Using .GetType() on an instance
Type type2 = (new Player()).GetType();
Once you have a Type , you can:
- Inspect its fields, properties and methods
- Check its attributes
- Invoke methods or create instances dynamically
How to use it?
Okay that’s enough of theory let’s code a simple player class and inspect it’s properties , methods and attributes at runtime.
Player class
[Serializable]
public class Player
{
public string Name { get; private set; }
public int Level { get; private set; }
public Player(string name, int level)
{
Name = name;
Level = level;
}
// parameterless constructor
public Player() { }
public void LevelUp()
{
Level++;
Debug.Log("Player leveled up");
}
}
Reflector class
private void Start()
{
Player player = new Player("Dude", 1);
// Get the Type metadata of Player
Type playerType = player.GetType();
// Get property value using Reflection
var level = playerType.GetProperty("Level").GetValue(player);
Debug.Log($"Level: {level}"); // Output: Level: 1
// Inspect all properties
PropertyInfo[] properties = playerType.GetProperties(
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
foreach (var property in properties)
{
Debug.Log($"Property Name: {property.Name}"); // Output: Name, Level
}
// Inspect and invoke methods with no parameters
MethodInfo[] methods = playerType.GetMethods(
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
foreach (var methodInfo in methods)
{
if (methodInfo.GetParameters().Length == 0)
{
Debug.Log($"Invoking: {methodInfo.Name}");
methodInfo.Invoke(player, null);
// Will call LevelUp and show "Player leveled up"
}
}
// Inspect class-level attributes
IEnumerable<Attribute> attributes = playerType.GetCustomAttributes();
foreach (var attribute in attributes)
{
Debug.Log($"Player class has an attribute named: {attribute.GetType().Name}");
// Output: Player class has an attribute named: SerializableAttribute
}
}
✨ And that’s it — now you’ve seen how Reflection works in C# and how you can use it in Unity to peek inside your own code!
It’s a handy tool for building smarter workflows, editor tools, or just satisfying your curiosity at runtime, but be careful about performance costs of it.
Stay tuned — in upcoming articles, we’ll build a simple yet extendable Developer Console using Reflection and Attributes.
✨ Thanks for reading!
buy me a coffee