When to use 'delegate', 'event' or 'Action' ?

Hi,

Can someone please provide a definitive explanation of when/in what circumstance to make use of each of these over the other?

I’d also be interested in hearing opinions on what the best practice might be moving forward (future Unity versions/new techniques, etc.).

Thanks in advance.

A delegate is a type that can contain any method of the same compatible type.
Delegates are used to define callback methods and implement event handling.
Any method from any accessible class or struct that matches the delegate type can be assigned to the delegate.
Example:

public delegate int Calculate(int x, int y);

An example method that can be assigned to this delegate:

public int Minus(int x, int y)
{
    return x - y;
}

An action is a premade delegate you can use without the need of making a custom one.
Action by itself has no parameters, but you can have those with the generic Action<T> version

Examples:

Action sayHello = () => { Debug.Log("Hello"); };
sayHello();
Action<string> print  = (a) => { Debug.Log(a); };
print("abc");

Usage:

You can pass these to your own methods as parameter

public void FindAndExecute(int number, Action toBeExecuted)
{
     //search list for number, and execute the toBeExecuted method
}

// If 5 is in list, then player will die.. idk..
FindAndExecute(5, () => { Player.Alive = false; });

You also have a few other premade delegates such as Func, which is the same as Action but with a return type like so:

Func<string> getValue = () => { return "value"; };
Func<string, bool> isValue = (a) => { return a == "value"; };

I often use Func as a custom criteria parameter to some methods
that retrieve data.

Events are also of the delegate type, which use the observer type pattern.
It allows a subscriber to register a method to an event, and be notified when the event is invoked.

Examples:

public event EventHandler PlayerJoinedLobby;

public void JoinLobby()
{
    PlayerJoinedLobby?.Invoke(); //Any subscriber is now notified that a player joined the lobby (method is executed on all subscribed objects)
}

// Any class can subscribe to this event like:
className.PlayerJoinedLobby += method;

// Unsubscribe:
className.PlayerJoinedLobby -= method;

Can be used to notify many listeners at once for a method to be executed.
Why use events? It keeps your classes decoupled, (single purpose classes)
which makes for more robust and clean code, and your subscribers
won’t affect the other class in anyway as they are independent on the behaviour of their subscribers.

All these topics are quite well explained on the microsoft docs, it is worth to check them out.