Value & Reference Types
Keywordsstruct, class, ref, out
CategoryType system
In UnityVector3, Quaternion, Color are structs; GameObject, Transform, your scripts are classes

Value & Reference Types

Every type in C# is either a value type (copied when you assign it) or a reference type (shared when you assign it). This one difference explains many confusing Unity errors, like why transform.position.x = 5 doesn't compile.

A value type is a photocopy of a document. Give someone a copy and they can scribble on it all they like; your original doesn't change. A reference type is a link to a shared online document. Give someone the link and you're both looking at the same document, so their edits show up for you too.

Which is which?

Value types (copied)Reference types (shared)
int, float, bool, double, charstring (special: behaves like a value, see below)
enum typesArrays (int[], GameObject[])
Any struct: Vector2, Vector3, Quaternion, Color, Ray, RaycastHitAny class: GameObject, Transform, Rigidbody, List<T>, your MonoBehaviours
Can't be null (unless you use int?)Can be null

Seeing the difference

// Value type: b gets its own copy
Vector3 a = new Vector3(1f, 2f, 3f);
Vector3 b = a;
b.x = 100f;
Debug.Log(a.x); // 1   (a is unchanged)

// Reference type: both variables point to the same object
List<int> listA = new List<int> { 1, 2, 3 };
List<int> listB = listA;
listB.Add(4);
Debug.Log(listA.Count); // 4   (it's the same list)
Value type (struct)Vector3 b = a; b.x = 100;Reference type (class)List<int> b = a; b.Add(4);ax = 1 y = 2 z = 3bx = 100 y = 2 z = 3two separate copies:changing b leaves a alonearefbrefList<int>1, 2, 3, 4one object on the heap:a and b both see the change
Assigning a struct copies the data; assigning a class copies only the reference, so both variables lead to the same object.

Why transform.position.x = 5 fails

transform.position is a property that returns a Vector3. Because Vector3 is a struct, you get back a copy. Changing x on a temporary copy would do nothing, so the compiler stops you with error CS1612.

// Error CS1612: Cannot modify the return value of 'Transform.position' because it is not a variable
transform.position.x = 5f;

// Fix: copy, change, assign back
Vector3 pos = transform.position;
pos.x = 5f;
transform.position = pos;

The same applies to rb.linearVelocity, material.color and any other struct property.

Passing to methods

void MakeBig(Vector3 v)        { v *= 10f; }         // changes a local copy only
void MakeBig(ref Vector3 v)    { v *= 10f; }         // changes the caller's variable
void Rename(GameObject go)     { go.name = "Big"; }  // changes the shared object

Vector3 size = Vector3.one;
MakeBig(size);        // size is still (1,1,1)
MakeBig(ref size);    // size is now (10,10,10)
Rename(gameObject);   // the real GameObject is renamed
KeywordMeaningUnity example
refPass the caller's variable itself, so the method can change itVector3.SmoothDamp(..., ref velocity, ...) remembers speed between frames
outThe method must fill this variable in. Used to return extra resultsPhysics.Raycast(ray, out RaycastHit hit), TryGetComponent(out Rigidbody rb)

Strings are special

string is a reference type, but it's immutable: you can never change a string, only make a new one. So it behaves like a value type in practice.

string a = "Hello";
string b = a;
b += " World";   // creates a NEW string; a is still "Hello"

Making your own struct

Use a struct for small bundles of data that you treat as a single value (like a coordinate or a damage number). Use a class for everything else.

[System.Serializable] // lets Unity show it in the Inspector
public struct DamageInfo
{
    public float amount;
    public Vector3 hitPoint;
    public bool isCritical;

    public DamageInfo(float amount, Vector3 hitPoint, bool isCritical)
    {
        this.amount = amount;
        this.hitPoint = hitPoint;
        this.isCritical = isCritical;
    }
}

// Usage
var info = new DamageInfo(25f, hit.point, Random.value < 0.1f);
target.TakeDamage(info);

What happens at runtime

  1. Value types are stored directly where they're declared: inside a local variable, or inline inside the object that has them as a field. Local value types need no garbage collection.
  2. Reference types live on the heap, a shared memory area. Your variable only holds the address. new on a class allocates heap memory, which the garbage collector must clean up later.
  3. This is why Unity's math types are structs: you can create millions of Vector3s per second without producing garbage.
  4. Boxing: if you store a value type in an object variable (or a non-generic collection), C# wraps it in a heap object. That creates garbage. Generic collections like List<int> avoid it.

Common Mistakes

See also