Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

The difference between += and Delegate.Combine

Tags:

c#

delegates

I've built an event system that maintains a dictionary of delegates, adds/removes elements to this dictionary via generic Subscribe/Unsubscribe methods (which each take an Action of type T), and has a Publish method to notify the subscribers when something happens (which also takes an Action of type T). Everything works fine, but I've noticed I cannot use += or -= when adding or removing elements to my dictionary, as the types passed into the methods (Action of T), do not match the types stored in the dictionary (Delegate). The following snippet shows what I can and cannot do.

private readonly Dictionary<Type, Delegate> delegates = new Dictionary<Type, Delegate>();

public void Subscribe<T>(Action<T> del)
{
    if (delegates.ContainsKey(typeof(T)))
    {
        // This doesn't work!
        delegates[typeof(T)] += del as Delegate;
        // This doesn't work!
        delegates[typeof(T)] += del;
        // This is ok
        delegates[typeof(T)] = (Action<T>)delegates[typeof(T)] + del;
        // This is ok
        var newDel = (Action<T>)delegates[typeof(T)] + del;
        delegates[typeof(T)] = newDel;
        // This is ok
        del += (Action<T>)delegates[typeof(T)];
        delegates[typeof(T)] = del;
        // This is ok
        delegates[typeof(T)] = Delegate.Combine(delegates[typeof(T)], del);
    }
    else
    {
        delegates[typeof(T)] = del;
    }
}

I mostly understand Jon Skeet's answer here += operator for Delegate specifically, this part

The binary + operator performs delegate combination when both operands are of some delegate type D. (If the operands have different delegate types, a binding-time error occurs.)

What I don't understand, is this

The compiler turns it into a call to Delegate.Combine. The reverse operation, using - or -=, uses Delegate.Remove.

What exactly is going on when I use += or -=, versus Delegate.Combine? What are the differences between the two, making one implementation valid, and another invalid?

like image 897
Chris Riley Avatar asked Apr 11 '19 08:04

Chris Riley


People also ask

What is the main difference between the event and delegate?

Delegate is a function pointer. It holds the reference of one or more methods at runtime. Delegate is independent and not dependent on events. An event is dependent on a delegate and cannot be created without delegates.

What is difference between delegate and method?

A delegate is a method with a parameter and a return type. A delegate is a type that safely encapsulates a method. Delegates are object-oriented, type safe, and secure.

What is the difference between events and multicast delegates?

Delegates are pointer to functions and used for call back. Multicast delegates help to invoke multiple callbacks. Events encapsulate delegate and implement publisher and subscriber model.

What is the correct way to define a delegate?

A delegate is a type that represents references to methods with a particular parameter list and return type. When you instantiate a delegate, you can associate its instance with any method with a compatible signature and return type. You can invoke (or call) the method through the delegate instance.


1 Answers

At the end of the answer you linked, it says:

Since System.Delegate is not a delegate type, operator + is not defined for it.

This explains why this does not work (both operands are Delegate):

delegates[typeof(T)] += del as Delegate;

Delegate.Combine works because it is declared like this:

public static Delegate Combine (params Delegate[] delegates);

Note how it accepts parameters of type Delegate, instead of a specific delegate type. And it will throw an ArgumentException if the delegates are not of the same type.

So the compiler not only changes the + operator to Delegate.Combine, it also does some type checking! On the other hand, no compile-time type checking is done if you use Delegate.Combine directly. Delegate.Combine only checks the types at runtime.

All the other lines work because you are casting i.e. telling the compiler what type the delegates are, making both operands of + to be of type Action<T>.

like image 186
Sweeper Avatar answered Oct 04 '22 01:10

Sweeper