C# Reflection: typeof in Practice
c# reflection typeof: Learn how to use typeof in C# reflection to obtain type metadata, inspect members, and apply reflection safely with performance considerations.
c# reflection typeof requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.
The typeof operator in C# returns a System.Type instance that describes the metadata of a type at compile time. It is the most direct way to obtain a Type object for a known type, and it is the foundation for most reflection code. When you write typeof(SomeClass), the compiler embeds the type reference directly, and the runtime resolves it to a Type object without requiring an instance. This makes typeof the starting point for any reflection task where you already know the type statically.
The typeof Operator and Type Objects
typeof is a C# operator that takes a type name as its operand and produces a System.Type instance. The type name can be a built-in type, a user-defined class, an interface, a delegate, or even a generic type definition. The resulting Type object exposes properties and methods that describe the type's structure, such as its name, namespace, base type, implemented interfaces, members, and attributes.
using System; public class SampleClass { public int Id { get; set; } public void DoWork() { } } Type sampleType = typeof(SampleClass); Console.WriteLine(sampleType.FullName); // Output: SampleClass Console.WriteLine(sampleType.IsClass); // Output: True
Because typeof is evaluated at compile time, the type reference is fixed. This is different from GetType(), which is called on an instance and returns the runtime type of that object. The distinction matters when you have a variable whose declared type differs from its actual runtime type, such as when using inheritance or interfaces.
Getting a Type from an Instance: typeof vs. GetType
When you have an object instance, you can call GetType() to obtain its runtime type. This is not the same as typeof, which requires a compile-time type name. Consider the following scenario:
object obj = new SampleClass(); Type fromGetType = obj.GetType(); // Runtime type: SampleClass Type fromTypeof = typeof(object); // Compile-time type: System.Object
obj.GetType() returns SampleClass because the actual object is a SampleClass. typeof(object) returns System.Object because that is the type specified in the code. This difference is critical when you need to inspect the concrete type of a polymorphic object. Use typeof when the type is known at compile time; use GetType() when you need the runtime type of an instance.
The following table summarizes the key differences:
| Aspect | typeof | GetType() |
|---|---|---|
| Operand | Type name (compile-time) | Object instance |
| Result | Type at compile time | Runtime type of the instance |
| Usage | Static type reference | Dynamic type discovery |
| Performance | No virtual call, compile-time | Virtual call, slight overhead |
Using typeof with Reflection APIs
Once you have a Type object from typeof, you can use reflection to inspect members, invoke methods, or read attributes. The Type class provides methods like GetMethods(), GetProperties(), GetFields(), and GetCustomAttributes(). These methods return metadata objects that describe the members.
using System; using System.Linq; using System.Reflection; Type type = typeof(SampleClass); MethodInfo[] methods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance); foreach (MethodInfo method in methods) { Console.WriteLine($"Method: {method.Name}"); } PropertyInfo[] properties = type.GetProperties(); foreach (PropertyInfo prop in properties) { Console.WriteLine($"Property: {prop.Name} ({prop.PropertyType.Name})"); }
This code lists all public instance methods and all properties of SampleClass. Reflection APIs are powerful but can be verbose. The Type object is the entry point; without typeof (or GetType()), you cannot access these metadata APIs.
Practical Example: Inspecting a Class at Runtime
A common use of c# reflection typeof is to build tools that analyze or manipulate types dynamically. For instance, you might want to discover which classes in an assembly implement a specific interface, or you might want to serialize an object without knowing its type in advance. Here is a minimal example that finds all public properties of a class and prints their names and types:
using System; using System.Reflection; public class Customer { public string Name { get; set; } public int Age { get; set; } public string Email { get; set; } } public static void InspectProperties(Type type) { PropertyInfo[] props = type.GetProperties(); foreach (PropertyInfo prop in props) { Console.WriteLine($"{prop.Name} : {prop.PropertyType}"); } } InspectProperties(typeof(Customer));
This pattern is useful for writing generic serializers, mapping objects to data tables, or building dynamic UI forms. The key is that typeof(Customer) gives you a Type object that can be passed to any method expecting Type.
Performance and Caching Considerations
Reflection is known to be slower than direct code because it involves runtime type inspection and often allocates arrays of metadata objects. The typeof operator itself is cheap because it simply returns a cached Type instance; the runtime maintains a metadata table for each loaded type. However, calling methods like GetMethods() or GetProperties() repeatedly can be expensive because each call may allocate new arrays and perform internal lookups.
If you need to inspect the same type multiple times, cache the results. For example, store the PropertyInfo[] array in a static dictionary keyed by Type to avoid repeated reflection calls. This is a common optimization in libraries that rely on reflection, such as ORMs and serializers.
private static readonly Dictionary<Type, PropertyInfo[]> PropertyCache = new(); public static PropertyInfo[] GetCachedProperties(Type type) { if (!PropertyCache.TryGetValue(type, out var props)) { props = type.GetProperties(); PropertyCache[type] = props; } return props; }
Even with caching, reflection should be used judiciously in hot paths. If you are building a high-throughput service, consider generating code at build time or using source generators instead of runtime reflection. The tradeoff is between flexibility and performance; reflection gives you dynamic behavior at the cost of speed.
Common Pitfalls and Edge Cases
One common mistake is using typeof on a generic type without specifying type arguments. For example, typeof(List<>) returns the generic type definition, while typeof(List<int>) returns the constructed type. These are different Type objects, and reflection methods behave differently on them.
Type openGeneric = typeof(List<>); Type closedGeneric = typeof(List<int>); Console.WriteLine(openGeneric.IsGenericTypeDefinition); // True Console.WriteLine(closedGeneric.IsGenericType); // True Console.WriteLine(openGeneric.GetGenericArguments()[0]); // T
Another edge case is nullable value types. typeof(int?) returns a Nullable<int> type, not int. This can cause unexpected behavior when comparing types. Use Nullable.GetUnderlyingType() if you need to work with the underlying type.
Also, be aware that typeof cannot be used with a variable; it requires a type name. If you have only a string containing the type name, you must use Type.GetType(string) or Assembly.GetType(). This is a different API and is not covered by typeof.
When to Prefer typeof Over Other Approaches
Choose typeof when you know the type at compile time and need a Type object for reflection, attribute lookup, or type comparison. It is the clearest and most efficient way to obtain a Type reference. Use GetType() when you have an instance and need its runtime type, especially in polymorphic scenarios. If you only have a string name, use Type.GetType() or assembly scanning.
For generic code, typeof(T) inside a generic method is a common pattern. It returns the Type of the type argument T at runtime, which is essential for generic reflection.
public static void PrintTypeName<T>() { Console.WriteLine(typeof(T).Name); } PrintTypeName<int>(); // Output: Int32 PrintTypeName<string>(); // Output: String
This pattern is widely used in logging, serialization, and dependency injection frameworks. The typeof operator is a fundamental tool in C# reflection, and understanding its behavior is essential for writing robust reflective code.