C# typeof vs GetType: Compile-Time vs Runtime Type Resolution
c# typeof vs gettype: Learn the difference between C#'s typeof operator and GetType() method, including compile-time vs runtime resolution, inheritance behavior, and p...
The typeof operator and the GetType() method both return a System.Type instance, but they answer different questions. When you compare c# typeof vs gettype, the fundamental distinction is compile-time resolution versus runtime discovery. typeof resolves a type at compile time from a type name. GetType() discovers the runtime type of an object instance at execution time. This difference drives every practical decision about which one to use.
The Core Difference: Compile-Time vs Runtime Type Resolution
typeof is a C# operator. The compiler knows exactly which type you mean when you write typeof(MyClass), and it emits a metadata token that loads the corresponding Type object. There is no instance involved and no runtime lookup.
GetType() is a method inherited from System.Object. Every reference type and value type in C# exposes it. When you call GetType() on an instance, the runtime examines the actual object that was allocated and returns its concrete type. The declared type of the variable holding the reference is irrelevant.
object data = "hello"; Type declared = typeof(object); // System.Object Type runtime = data.GetType(); // System.String
typeof(object) returns the Object type because that is the compile-time operand. data.GetType() returns String because that is the type of the object the variable points to.
Using typeof to Resolve a Type at Compile Time
The typeof operator takes a type name as its operand. The type must be known at compile time. You cannot use typeof with an instance, a variable, or an expression that evaluates to a value.
Type stringType = typeof(string); Type listType = typeof(List<int>); Type serviceType = typeof(MyService);
The operand can be any type: a built-in type, a custom class, an interface, a generic type, or a generic type parameter. This makes typeof particularly useful in generic methods where the type parameter is known at compile time.
public void Process<T>() { Type genericType = typeof(T); // Use the type for reflection or validation }
Because typeof is resolved by the compiler, it produces a constant Type reference. The emitted IL uses the ldtoken instruction to load the metadata token for the type. There is no method call, no virtual dispatch, and no allocation beyond the runtime's cached Type object.
Using GetType() to Discover the Runtime Type of an Instance
GetType() is defined on System.Object, so every type inherits it. When you call it on an instance, the runtime returns the most derived type of that instance.
object value = "hello"; Type runtimeType = value.GetType(); Console.WriteLine(runtimeType); // System.String
The key behavior is that GetType() returns the actual type of the object, not the declared type of the variable. If you declare a variable as object but assign a List<int> to it, GetType() returns List<int>.
object data = new List<int>(); Console.WriteLine(data.GetType()); // System.Collections.Generic.List`1[System.Int32]
This is the fundamental distinction: typeof tells you what the compiler knows, while GetType() tells you what the runtime actually created.
How Inheritance Changes What GetType() Returns
Inheritance makes the difference visible immediately. Consider a base class and a derived class:
public class Animal { } public class Dog : Animal { } Animal pet = new Dog(); Type staticType = typeof(Animal); // System.Animal Type runtimeType = pet.GetType(); // System.Dog
typeof(Animal) always returns the Animal type, regardless of what instance is assigned to the variable. pet.GetType() returns Dog because that is the type of the object that was actually allocated.
This behavior is critical in logging, serialization, and dependency injection scenarios, where you need the concrete type of an object rather than the declared type of a variable. A logging framework that calls GetType() on an exception records the real exception type, not the base Exception type the handler declared.
Performance and Runtime Cost of Each Approach
typeof has essentially no runtime cost. The type reference is resolved at compile time, and the emitted IL loads a metadata token. There is no method dispatch and no heap allocation beyond the runtime's cached Type object.
GetType() is a virtual method call. The runtime must follow the object's type handle to resolve the actual type. This is fast, but it is not free. In hot paths where type checks execute millions of times, the difference between typeof and GetType() can be measured, though it is rarely the bottleneck in real applications.
The more important consideration is correctness, not speed. Using typeof when you need the runtime type produces wrong results in polymorphic scenarios. Using GetType() when you only need the compile-time type adds unnecessary indirection and throws when the instance is null.
When to Use typeof vs GetType()
Use typeof when you have a type name and need its Type object. This covers reflection utilities, generic type parameters, and compile-time type comparisons.
public void Register<T>() { Type type = typeof(T); container.Register(type); }
Use GetType() when you have an object instance and need its concrete runtime type. This covers logging, serialization, and polymorphic dispatch.
public void Log(object value) { Type actualType = value.GetType(); logger.Log($"Processing {actualType.FullName}"); }
A common mistake is reaching for GetType() when the is operator or pattern matching expresses the intent more clearly. If you only need to check whether an object is a specific type, obj is MyClass is more readable and handles null safely. Reserve GetType() for cases where you genuinely need the Type object itself.
Edge Cases: Null References, Interfaces, and Generic Type Parameters
Calling GetType() on a null reference throws a NullReferenceException. There is no instance to inspect, so the runtime has no type to return. Guard against null before calling GetType().
object value = null; // Throws NullReferenceException // Type t = value.GetType();
Interfaces behave differently. GetType() never returns an interface type because an interface is not a concrete type. If an object implements IDisposable, GetType() returns the concrete class, not IDisposable. To check interface implementation, use the is operator or typeof(IDisposable).IsAssignableFrom(obj.GetType()).
Generic type parameters work with typeof because the type is known at compile time within the generic method. GetType() cannot be used with a type parameter directly because it requires an instance.
public void Inspect<T>(T value) { Type parameterType = typeof(T); // Compile-time type Type instanceType = value.GetType(); // Runtime type, may differ }
When T is an interface or a base class, typeof(T) returns that interface or base type, while value.GetType() returns the concrete implementation. This distinction is exactly what you need when building generic utilities that must separate the declared contract from the actual implementation. Choosing between typeof and GetType() comes down to one question: do you have a type name or an instance? The answer determines which API is correct for the job.