Summary
An interface is a list of method (and property) signatures with no implementation. Any class that implements an interface promises to provide concrete implementations of everything on that list. Where csharp-inheritance says “I can do these things because my parent can,” an interface says “I can do these things because I have guaranteed it.” Interfaces enable programming to an abstraction — code that depends on an interface works with any class implementing it, without caring about internal details.
Unity itself uses interfaces extensively: IPointerClickHandler, IBeginDragHandler, IDamageable are common patterns. A class can implement as many interfaces as needed but can only have one parent class.
Key ideas
Declaring an interface:
By convention, interface names start with a capital I.
public interface IDamageable
{
void TakeDamage(int amount);
int GetHealth();
}No access modifiers on members, no method bodies — just signatures. Every implementing class must provide these methods.
Implementing an interface:
public class Player : MonoBehaviour, IDamageable
{
private int health = 100;
public void TakeDamage(int amount)
{
health -= amount;
if (health <= 0) Die();
}
public int GetHealth() => health;
private void Die() { /* ... */ }
}
public class DestructibleCrate : MonoBehaviour, IDamageable
{
private int health = 30;
public void TakeDamage(int amount)
{
health -= amount;
if (health <= 0) Destroy(gameObject);
}
public int GetHealth() => health;
}Player and DestructibleCrate are completely unrelated classes (different parents), but both implement IDamageable. Anything that needs to deal damage does not need to know which it is dealing with:
// Works for any IDamageable — Player, Crate, Boss, etc.
void DealDamage(IDamageable target, int amount)
{
target.TakeDamage(amount);
}Multiple interfaces:
public interface IInteractable
{
void Interact();
string GetPrompt();
}
public class Chest : MonoBehaviour, IDamageable, IInteractable
{
// Must implement TakeDamage + GetHealth + Interact + GetPrompt
public void TakeDamage(int amount) { /* ... */ }
public int GetHealth() => health;
public void Interact() { Open(); }
public string GetPrompt() => "Open chest";
private int health = 50;
private void Open() { /* ... */ }
}This allows Chest to be treated as IDamageable by combat code and as IInteractable by the player interaction system — two completely different contexts.
Interface variables:
A variable of an interface type can hold any object that implements that interface.
IDamageable target = FindObjectOfType<Player>(); // or any IDamageable
target.TakeDamage(10);Properties in interfaces:
Interfaces can declare properties as well as methods.
public interface IHasName
{
string Name { get; }
}Interface vs abstract class
| Interface | Abstract class | |
|---|---|---|
| Method implementations | None (pre-C# 8) | Can include fully implemented methods |
| Multiple inheritance | A class can implement many | A class can only extend one |
| Fields/state | Not allowed | Allowed |
| When to use | Define a capability contract across unrelated classes | Define a shared base type with some shared logic |
Use an interface when: disparate, unrelated classes need to share a behaviour contract (e.g., anything damageable, anything interactable).
Use an abstract class when: related classes share a significant amount of implementation that would be wasteful to duplicate (e.g., all enemies share combat logic).
These are not mutually exclusive — a class can extend an abstract base class and implement interfaces.
In practice (Unity)
Unity’s built-in interfaces:
Unity’s EventSystem uses interfaces for UI callbacks. Any MonoBehaviour that implements them receives the corresponding events:
using UnityEngine.EventSystems;
public class ClickableButton : MonoBehaviour, IPointerClickHandler, IPointerEnterHandler
{
public void OnPointerClick(PointerEventData data)
{
Debug.Log("Clicked!");
}
public void OnPointerEnter(PointerEventData data)
{
Debug.Log("Hovered!");
}
}No subclassing required — attaching this component is enough. Unity’s EventSystem reflects on the component and calls the interface methods automatically.
Finding and using damageable targets:
// Get the IDamageable from whatever object was hit
void OnCollisionEnter2D(Collision2D col)
{
IDamageable target = col.gameObject.GetComponent<IDamageable>();
if (target != null)
{
target.TakeDamage(damage);
}
}GetComponent<IDamageable>() works with any component implementing the interface — no need to check for Player, Enemy, Crate separately.
Evidence
From Miles (2019, §4.8): “Interface: ‘I can do these things because I have told you I can.’ Inheritance: ‘I can do these things because my parent can’.”
From Miles (2019, §4.8.7): “Interfaces are a way that you can bring together a set of behaviours. They are packaged as a list of methods which a class must contain if it implements the interfaces.”
Gotchas
- If a class implements an interface but does not implement every member, the compiler will reject it with an error.
- Interface methods are implicitly
public— do not add an access modifier when implementing them; doing so causes a compile error. - Checking if an object implements an interface: use
isoras.obj as IDamageablereturnsnullif the cast fails;obj is IDamageable dboth tests and assigns. - In older Unity versions (pre-2019.4),
GetComponent<IInterface>()had limited support — ensure your Unity version handles it correctly. Modern Unity handles it fine.
Related
- csharp-oop-fundamentals — classes and access modifiers
- csharp-inheritance — abstract classes, virtual methods
- csharp-properties — properties in interface contracts
- unity-getcomponent — GetComponent with interfaces
- unity-object-communication — how scripts communicate without tight coupling