5

Are these 2 samples the same? Can old-style raising be replaced with Invoke and null propagation?

OLD:

    public event EventHandler<MyEventArgs> MyEvent;
    protected virtual void OnMyEvent(MyEventArgs args)
    {
        EventHandler<MyEventArgs> handler = this.MyEvent;
        if (handler != null)
            handler(this, args);
    }

NEW:

    public event EventHandler<MyEventArgs> MyEvent;
    protected virtual void OnMyEvent(MyEventArgs args)
    {
        this.MyEvent?.Invoke(this, args);
    }

Null check is important but it is clear. What is about additional variable?
How does null-propogation work internally? Is it thread-safe with events?

P.S. Regarding thread safety in events you can read here:
C# Events and Thread Safety

Maxim
  • 13,029
  • 6
  • 30
  • 45
  • Related: https://codeblog.jonskeet.uk/2015/01/30/clean-event-handlers-invocation-with-c-6/ – VMAtm Feb 12 '17 at 22:29
  • Also, links in russian: https://habrahabr.ru/post/89529/ and https://habrahabr.ru/post/240385/ – VMAtm Feb 12 '17 at 22:37
  • It is not duplicate.. at least thread-safety is specific topic in this question.. and in previous post answer says something strange "compiler generates code to evaluate PropertyChanged one time only, keeping the result in temporary variable".. It does nothing for thread-safety. Only the fact of copying is matter! And that was investigated by @eocron. So it makes difference.. to understand what exactly happens it is much better to get answer from this post than previous one. – Maxim Feb 13 '17 at 00:21

1 Answers1

9

Given this code:

    static void Main(string[] args)
    {
        var a = new Random(1).Next() > 0 ? new object() : null;
        var b = a?.GetHashCode();
        Console.WriteLine(b);
    }

This is what I see in IL representation at line where monadic operator is called in Release mode (VS 2015):

IL_0016: dup          
IL_0017: brtrue.s     IL_0025

... //nothing iteresting, just setting null to 'a' and skip IL_0025 area

IL_0025: callvirt     instance int32 [mscorlib]System.Object::GetHashCode()

Let me explain:

  • dup - is command which copies current value on stack into new one placed in this stack (in this case it is simply duplicating value of 'a')
  • brtrue.s - transfers control to address if value on stack is true/notnull/non-zero (as of now value is copy of 'a' - it is thread-safe to use it)

So, answer to your question is: YES, it is thread-safe to use monadic operator, because it is operating over a copy.

eocron
  • 6,885
  • 1
  • 21
  • 50
  • Thanks for IL work! Pretty clear now... it will be a copy. – Maxim Feb 12 '17 at 21:26
  • This version will not break, but still isn't a thread-safe solution, in theory. In practice, it's OK-level solution – VMAtm Feb 12 '17 at 22:38
  • Raising of event itself is thread-safe... thread safety of any other code is not related. – Maxim Feb 13 '17 at 00:10
  • In theory you can call already unsubscribed class – VMAtm Feb 13 '17 at 02:12
  • 1
    Yes, but as I said above it is not related to thread-safety of raising. It is thread-safety of subscribed code. Feel the difference: "raising" and "subscribing / unsubscribing". Of course you can lock subscribing / unsubscribing + raising (so all handlers will be impacted). But why can it be required? Only to be sure that code in handler will not be executed any more after unsubscribing. And it is exactly thread-safety of client code... If you mean events are not thread safe.. Yes obviously.. but raising how it is described above is thread safe. – Maxim Feb 13 '17 at 12:58