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()
GetType() is an instance method available on all objects.
Type t = obj.GetType();
This returns the object's exact runtime type.
Important:
- It only works if
objis notnull - It does not match base classes or interfaces when compared directly
Example:
class Animal {}
class Dog : Animal {}
Animal a = new Dog();
Console.WriteLine(a.GetType() == typeof(Animal)); // false
Console.WriteLine(a.GetType() == typeof(Dog)); // true
is
The is operator checks whether a value is compatible with a given type.
if (obj is int)
{
// obj can be treated as int
}
This includes:
- exact type matches
- derived types
- implemented interfaces
- pattern matching forms
Example:
class Animal {}
class Dog : Animal {}
Animal a = new Dog();
Console.WriteLine(a is Animal); // true
Console.WriteLine(a is Dog); // true
This is why is is often the best choice when your goal is: "Can I safely use this as that type?"
Why this matters
In real programs, type checks affect:
- inheritance behavior
- interface handling
- null safety
- readability
- correctness of branching logic
Choosing the wrong form can introduce subtle bugs. For example, using GetType() == typeof(BaseClass) can reject derived objects even when they should be accepted.
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()returnsDog, because the object created at runtime is aDogpet is Animalistruebecause aDogcan be used anywhere anAnimalis expected
Example: safer modern is pattern
object value = 42;
if (value is int number)
{
Console.WriteLine(number + 10);
}
Why this is useful
This checks the type and gives you a strongly typed variable number in one step.
Example: comparing against a known type
Type targetType = typeof(string);
Console.WriteLine(targetType.Name); // String
This is a common use of typeof in reflection or metadata-based code.
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:Dogtypeof(Animal)is theTypeobject forAnimalDogis not exactlyAnimal- result:
false
3. pet.GetType() == typeof(Dog)
pet.GetType()is stillDogtypeof(Dog)is alsoDog- exact match
- result:
true
4. pet is Animal
- A
Doginherits fromAnimal - therefore a
Dogcan be treated as anAnimal - result:
true
5. pet is Dog
- The object is actually a
Dog - result:
true
Output
False
True
True
True
This example shows the central rule:
GetType() == typeof(T)checks exact runtime typeis Tchecks whether the object is assignable toT
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
- dependency injection registration
- generic metadata handling
Type controllerType = typeof(HomeController);
Example from validation logic
if (input is string text)
{
Console.WriteLine(text.Trim());
}
This is clearer than checking exact type unless you specifically want to reject derived or compatible forms.
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))
{
// Only this exact type, not subclasses
}
This is less common in business logic, but useful when exact type identity matters.
Guard clause example
if (value is not string text)
{
return;
}
Console.WriteLine(text.Length);
This is a clean modern C# pattern for validation and early return.
Error handling example
catch (Exception ex) when (ex is InvalidOperationException)
{
Console.WriteLine("Invalid operation");
}
In many codebases, is is preferred in conditional logic because it communicates meaning more directly than manual type comparisons.
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() == typeof(string))
{
Console.WriteLine("string");
}
Problem
This throws a NullReferenceException.
Fix
Check for null first, or use is:
if (obj is string)
{
Console.WriteLine("string");
}
obj is string safely returns false when obj is null.
3. Using typeof as if it inspects an object
Broken idea:
// typeof does not read the runtime type of obj
Type t = typeof(string);
Clarification
typeof(string) always means the Type for string. It does not look at any variable's current value.
4. Choosing exact type comparison when polymorphism is intended
If your design uses base classes or interfaces, exact type checks can make the code too rigid.
Broken example:
if (payment.GetType() == typeof(IPaymentMethod))
{
// not meaningful for interface compatibility checks
}
Better
if (payment is IPaymentMethod method)
{
method.Process();
}
5. Forgetting that is can also declare a variable
Many beginners write:
if (obj is string)
{
string text = (string)obj;
Console.WriteLine(text.Length);
}
This works, but modern C# is cleaner:
if (obj is string text)
{
Console.WriteLine(text.Length);
}
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 is T
| Expression | Meaning |
|---|---|
obj.GetType() == typeof(T) | The object is exactly T |
obj is T | The object is T, or a subtype, or implements T |
Which one should you prefer?
Prefer is when:
- you want to know if an object can be used as a type
- inheritance or interfaces should count
- you want readable branching logic
Prefer GetType() when:
- only the exact runtime type should match
- derived types must be excluded
Prefer typeof when:
- you need a
Typeinstance from a known type name - you are doing reflection or metadata work
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
If you are writing normal conditional logic, is is usually the best default because it is readable and works well with inheritance.
Common patterns
if (obj is string text)
{
Console.WriteLine(text);
}
if (obj != null && obj.GetType() == typeof(MyType))
{
Console.WriteLine("Exact match");
}
Type t = typeof(MyType);
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?
No. is checks compatibility, while GetType() == typeof(T) checks exact type equality.
Which is most readable in everyday code?
Usually is, especially with pattern matching like if (obj is MyType value).
Should I avoid exact type checks?
Not always. Use exact checks when your logic truly depends on the object being that specific runtime type and not a subtype.
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.
Best Way to Repeat a Character in C#: Building Repeated Strings Efficiently
Learn the best way to repeat a character in C#, compare StringBuilder, string concatenation, and simpler built-in options.
C# Access Modifiers and static: public, private, protected, and Defaults
Learn how C# public, private, protected, and default access control visibility, and how static differs from instance members.