Types & Type Checking
KeywordsGetType(), typeof, is, as, (T) cast, nameof, var
CategoryType system
In UnityRequireComponent(typeof(Rigidbody)), if (other is IDamageable d)

Types & Type Checking

Sometimes you have a variable of a general type (Component, Enemy, object) and need to know what it actually is, or treat it as something more specific. C# gives you several tools: GetType(), typeof, is, as and casts.

A parcel labeled "Package". GetType() is reading the exact label inside: "Blue Ceramic Mug". is is asking "is this some kind of Mug?" (yes for a blue mug, a red mug or a travel mug). as is "open it as a mug; if it's not a mug, give me nothing". A cast is "this IS a mug, trust me", and if you're wrong, it throws an error.

Quick reference

ToolReturnsIf the type doesn't matchExample
obj.GetType()The exact runtime Typen/aenemy.GetType().Name → "Goblin"
typeof(T)The Type for a class name you writen/atypeof(Rigidbody)
obj is Tboolfalseif (enemy is Goblin)
obj is T tbool + a typed variablefalse, t unusableif (enemy is Goblin g) g.Taunt();
obj as TT or nullnullGoblin g = enemy as Goblin;
(T)objTThrows InvalidCastExceptionGoblin g = (Goblin)enemy;
nameof(x)The name as a stringCompile error if it doesn't existInvoke(nameof(Respawn), 2f)

GetType() vs typeof

Both give you a System.Type object that describes a type. The difference is when the type is known.

Enemy e = GetComponent<Enemy>();     // the variable's type is Enemy...
Debug.Log(e.GetType().Name);         // ..."Goblin": GetType() asks the OBJECT at runtime
Debug.Log(typeof(Enemy).Name);       // "Enemy": typeof() is decided when you write the code

// Exact type check (subclasses do NOT match)
if (e.GetType() == typeof(Goblin)) { }

// Where Unity wants a Type
[RequireComponent(typeof(Rigidbody))]
public class Hover : MonoBehaviour { }

Component c = gameObject.AddComponent(typeof(BoxCollider)); // non-generic version
GetType() vs ise.GetType() == typeof(Enemy) is false for a Goblin, because it checks the exact type. e is Enemy is true for a Goblin, because a Goblin is a kind of Enemy. You almost always want is.

is: check and convert in one step

void OnTriggerEnter(Collider other)
{
    Component c = other.GetComponent<Enemy>();

    // Pattern matching: if it's a Boss, give me a Boss variable called "boss"
    if (c is Boss boss)
    {
        boss.StartPhaseTwo();
    }
    else if (c is Enemy enemy)
    {
        enemy.TakeDamage(10);
    }
}

Modern C# also allows is not: if (target is not null). But for Unity objects, prefer != null; see the warning below.

switch on type

string Describe(Item item)
{
    switch (item)
    {
        case Weapon w when w.damage > 50: return "A deadly weapon";
        case Weapon w:                    return $"A weapon ({w.damage} dmg)";
        case Potion p:                    return $"A potion that heals {p.healAmount}";
        case null:                        return "Nothing";
        default:                          return item.name;
    }
}

Casting numbers

Casts also convert between number types. Going to a "smaller" type needs an explicit cast and can lose information.

float f = 3.9f;
int i = (int)f;               // 3: casting cuts off the decimals (doesn't round)
int r = Mathf.RoundToInt(f);  // 4: rounds properly

int hp = 37, max = 120;
float bad  = hp / max;        // 0: int / int is integer division, THEN converted
float good = (float)hp / max; // 0.308...

var

var lets the compiler figure out the type from the right-hand side. The variable still has a fixed type; you just don't write it.

var rb = GetComponent<Rigidbody>();   // rb is a Rigidbody
var enemies = new List<Enemy>();     // enemies is a List<Enemy>
// var x;                             // error: nothing to infer from

What happens at runtime

  1. Every object remembers its real type. GetType() reads it; is, as and casts compare against it.
  2. typeof and nameof are resolved when the code compiles, so they cost nothing at runtime.
  3. is, as and casts are fast, but GetType() plus reflection (reading methods and fields by name through Type) is slow. Avoid reflection in Update.

Common Mistakes

See also