Question
In C#, I have seen several ways to check types:
Type t = typeof(SomeType);
if (t == typeof(int))
{
// Some code here
}
I also know this can be written using GetType():
if (obj1.GetType() == typeof(int))
{
// Some code here
}
Or by using the is operator:
if (obj1 is int)
{
// Some code here
}
The is version looks the cleanest to me, but I am not sure whether these approaches are truly equivalent. What are the differences between typeof, GetType(), and is in C#? When should each one be used, and which is generally the best choice?
Short Answer
By the end of this page, you will understand what typeof, GetType(), and is actually do in C#, how they differ, and when each one is appropriate. You will also learn the important difference between checking an exact type and checking whether a value can be treated as a type, especially when inheritance is involved.
Concept
In C#, typeof, GetType(), and is are related to types, but they answer different questions.
typeof(T)asks: "What is theTypeobject for this known type name?"obj.GetType()asks: "What is the object's exact runtime type?"obj is Tasks: "Can this object be treated as aT?"
These are not interchangeable in all situations.
typeof
typeof is used with a type name known at compile time.
Type t = typeof(int);
This does not inspect an object instance. It simply gives you the System.Type metadata for a type.
Use it when you need:
- reflection
- type comparison against known types
- generic type metadata
- attributes or framework code
GetType()
Mental Model
Think of type checking like checking a person's role in a company.
typeof(Employee)is like looking up the job description document forEmployee.person.GetType()is like asking: "What is this person's exact job title right now?"person is Employeeis like asking: "Does this person count as an employee for this rule?"
If someone is a Manager, then:
- their exact type is
Manager - they are also an
Employee
So:
GetType() == typeof(Employee)would befalseis Employeewould betrue
That is the key difference: exact identity versus type compatibility.
Syntax and Examples
Core syntax
// 1. Get Type metadata from a known type name
Type t1 = typeof(int);
// 2. Get exact runtime type from an object instance
Type t2 = obj.GetType();
// 3. Check whether an object is compatible with a type
if (obj is int)
{
// use it as an int
}
Example: exact type vs compatible type
using System;
class Animal {}
class Dog : Animal {}
class Program
{
static void Main()
{
Animal pet = new Dog();
Console.WriteLine(pet.GetType() == typeof(Animal)); // false
Console.WriteLine(pet.GetType() == typeof(Dog)); // true
Console.WriteLine(pet is Animal); // true
Console.WriteLine(pet is Dog); // true
}
}
Explanation
pet.GetType()returns , because the object created at runtime is a
Step by Step Execution
Consider this example:
using System;
class Animal {}
class Dog : Animal {}
class Program
{
static void Main()
{
Animal pet = new Dog();
Console.WriteLine(pet.GetType() == typeof(Animal));
Console.WriteLine(pet.GetType() == typeof(Dog));
Console.WriteLine(pet is Animal);
Console.WriteLine(pet is Dog);
}
}
Step by step
1. Animal pet = new Dog();
- The variable type is
Animal - The actual object created is
Dog
This distinction is important:
- reference type:
Animal - runtime type:
Dog
2. pet.GetType() == typeof(Animal)
pet.GetType()returns the exact runtime type:
Real World Use Cases
When to use is
Use is when your code needs to know whether a value can be used as a certain type.
Examples
- handling different request types in an API
- checking whether an exception is a specific exception type or derived from it
- working with interface-based designs
- validating deserialized objects
if (service is IDisposable disposable)
{
disposable.Dispose();
}
When to use GetType()
Use GetType() when the exact runtime type matters.
Examples
- serialization frameworks
- logging exact object types
- strict type-based branching
- debugging tools
Console.WriteLine(obj.GetType().FullName);
When to use typeof
Use typeof when you need a Type object for a known type name.
Examples
- reflection
- attribute lookup
Real Codebase Usage
In real projects, developers usually choose based on intent.
Common pattern: use is for branching logic
if (command is CreateUserCommand createUser)
{
HandleCreateUser(createUser);
return;
}
This is common because it is:
- readable
- safe
- concise
- inheritance-friendly
Common pattern: use typeof in framework or infrastructure code
var handlers = new Dictionary<Type, object>();
handlers[typeof(string)] = new StringHandler();
handlers[typeof(int)] = new IntHandler();
This appears in:
- dependency injection
- event handler registration
- serializers
- mappers
Common pattern: use GetType() for exact matching
if (obj != null && obj.GetType() == typeof(SpecialCaseMessage))
{
}
Common Mistakes
1. Assuming GetType() == typeof(BaseType) matches derived classes
Broken example:
class Animal {}
class Dog : Animal {}
Animal pet = new Dog();
if (pet.GetType() == typeof(Animal))
{
Console.WriteLine("This will not run");
}
Why it fails
GetType() returns Dog, not Animal.
Fix
Use is if you want compatibility:
if (pet is Animal)
{
Console.WriteLine("This runs");
}
2. Calling GetType() on null
Broken example:
object obj = null;
if (obj.GetType() == ())
{
Console.WriteLine();
}
Comparisons
| Approach | What it checks | Works with inheritance/interfaces? | Null-safe? | Common use |
|---|---|---|---|---|
typeof(T) | Gets the Type object for a known type | Not a runtime compatibility check | Yes | Reflection, metadata, registration |
obj.GetType() | Object's exact runtime type | No, not when compared for equality | No | Exact type matching, diagnostics |
obj is T | Whether object is compatible with T | Yes | Yes | Branching, validation, pattern matching |
GetType() == typeof(T) vs
Cheat Sheet
typeof(int) // Type metadata for int
obj.GetType() // Exact runtime type of obj
obj is int // True if obj can be treated as int
obj is int number // Type check + typed variable
obj is not null // Null check using pattern syntax
Quick rules
- Use
typeof(T)for known type metadata - Use
obj.GetType()for exact runtime type - Use
obj is Tfor compatibility checks GetType()throws ifobjisnullisreturnsfalsefornullGetType() == typeof(BaseType)does not match subclassesis BaseTypedoes match subclasses
Best default choice
FAQ
Is is faster than GetType() in C#?
In most application code, performance differences are usually not the deciding factor. Choose based on correctness and clarity first.
Does is check derived classes in C#?
Yes. is returns true for compatible derived classes and implemented interfaces.
Does GetType() return the declared variable type?
No. It returns the object's actual runtime type.
Can GetType() be called on null?
No. Calling GetType() on null throws a NullReferenceException.
When should I use typeof in C#?
Use typeof when you need the Type object for a known type, especially for reflection or metadata-based code.
Are obj is T and obj.GetType() == typeof(T) the same?
Mini Project
Description
Build a small console program that classifies objects using typeof, GetType(), and is. This project helps you see the difference between exact type checks and compatibility checks in a practical way, especially with inheritance and interfaces.
Goal
Create a C# console app that prints how different type-checking approaches behave for strings, integers, derived classes, interfaces, and null values.
Requirements
- Create at least one base class and one derived class.
- Create at least one interface implemented by a class.
- Test several values, including
null, a primitive value, and an object stored in a base-class variable. - For each value, show the result of
isand, when safe,GetType() == typeof(...). - Include at least one example using
typeofto print type metadata.
Keep learning
Related questions
AddTransient vs AddScoped vs AddSingleton in ASP.NET Core Dependency Injection
Learn the differences between AddTransient, AddScoped, and AddSingleton in ASP.NET Core DI with examples and practical usage.
C# Version Numbers Explained: C# vs .NET Framework and Why “C# 3.5” Is Incorrect
Learn the correct C# version numbers, how they map to .NET releases, and why terms like C# 3.5 are inaccurate and confusing.
C# foreach Closure Behavior Explained: Why Loop Variables Were Reused
Learn why C# foreach loop variables once caused closure bugs, how the compiler handled scope, and what changed in newer C# versions.