I asked this question and got this interesting (and a little disconcerting) answer.
Daniel states in his answer (unless I\'m readin
In CLR via C# (pp. 264–265), Jeffrey Richter discusses this specific problem, and acknowledges that it is possible for the local variable to be swapped out:
[T]his code could be optimized by the compiler to remove the local […] variable entirely. If this happens, this version of the code is identical to the [version that references the event/callback directly twice], so a
NullReferenceExceptionis still possible.
Richter suggests the use of Interlocked.CompareExchange<T> to definitively resolve this issue:
public void DoCallback() 
{
    Action local = Interlocked.CompareExchange(ref _Callback, null, null);
    if (local != null)
        local();
}
However, Richter acknowledges that Microsoft’s just-in-time (JIT) compiler does not optimize away the local variable; and, although this could, in theory, change, it almost certainly never will because it would cause too many applications to break as a result.
This question has already been asked and answered at length in “Allowed C# Compiler optimization on local variables and refetching value from memory”. Make sure to read the answer by xanatox and the “Understand the Impact of Low-Lock Techniques in Multithreaded Apps” article it cites. Since you asked specifically about Mono, you should pay attention to referenced “[Mono-dev] Memory Model?” mailing list message:
Right now we provide loose semantics close to ecma backed by the architecture you're running.
This code will not throw a null reference exception. This one is thread safe:
public void DoCallback() {
    Action local;
    local = Callback;
    if (local == null)
        local = new Action(() => { });
    local();
}
The reason this one is thread-safe, and can-not throw a NullReferenceException on Callback, is it's copying to a local variable before doing it's null check / call. Even if the original Callback was set to null after the null check, the local variable will still be valid.
However the following is a different story:
public void DoCallbackIfElse() {
    if (null != Callback) Callback();
    else new Action(() => { })();
}
In this one it's looking at a public variable, Callback can be changed to null AFTER the if (null != Callback) which would throw an exception on Callback();