Skip navigation.

Signs of Aging DellAll recent postsStripping SPAN from WebControl

Chaining Delegates with Return Values

While reading Developing Application Frameworks in .NET by Xin Chen I came upon an interesting gotcha with chained delegates when each of them returns a value.

Suppose we have the following delegate:

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

This delegate can handle a method which takes two numbers, performs their addition, multiplication, etc, and returns a result. Let’s define a contrived class with each method having the signature of our delegate.

public class Math
{
 public int Sum (int x, int y) { return x + y; }
 public int Multiply (int x, int y) { return x * y; }
 public int Subtract (int x, int y) { return x - y; }
}

Delegate Backgrounder

The good thing about delegates is that you can chain them. The reason this is possible is because the declaration of a delegate expands into a class of its own. This is what you see in a disassembled form:

.class public CalculationHandler
   extends System.MulticastDelegate
{
.method public instance void .ctor(
    objectobject’, native int ’method’) runtime managed {}

.method public virtual instance System.IAsyncResult BeginInvoke(
    int32 x, int32 y, System.AsyncCallback callback, 
    object ’object’) runtime managed {}

.method public virtual instance int32 EndInvoke(
    System.IAsyncResult result) runtime managed {}

.method public virtual instance int32 Invoke(
    int32 x, int32 y) runtime managed {}
}

(I removed keywords irrelevant to this discussion.) That’s right—you get a whole class tailored specifically to the signature of your delegate. As a bonus, you get two methods for asynchronous invocation: BeginInvoke and EndInvoke.

Also note—the class, CalculationHandler, is derived from System.MulticastDelegate. By definition, a multicast delegate can have more than one element in its invocation list, and therefore you can chain delegates like this:

Math math = new Math();
CalculationHandler allTargets = null;

CalculationHandler sumHandler = 
    new CalculationHandler (math.Sum);

CalculationHandler multiplyHandler = 
    new CalculationHandler (math.Multiply);

CalculationHandler substractionHandler = 
    new CalculationHandler (math.Subtract); 

allTargets += sumHandler;
allTargets += multiplyHandler;
allTargets += substractionHandler;

Behind the scenes these delegates are “combined” in a linked list. Compiled, the last three lines become:

allTargets = (CalculationHandler) Delegate.Combine(
    allTargets, sumHandler);

allTargets = (CalculationHandler) Delegate.Combine(
    allTargets, multiplyHandler);

allTargets = (CalculationHandler) Delegate.Combine(
    allTargets, substractionHandler);

Those, who are extra curious, may inspect the CombineImpl method in the MulticastDelegate class to see how “chaining” works under the hood.

Back to my point. When we invoke this multicast delegate, the CRL calls each delegate in the order it was received… I mean, submitted, and the final result reflects the invocation of the last delegate in the chain. In other words the following line

int result = allTargets (4, 3);

causes CLR to call math.Sum, math.Multiply, math.Subtract, and the value of result is 1, because that’s what the last method, math.Subtract, returned. This is, probably, not what you expected.

Instead, you want to invoke every delegate by hand and handle the result of each one:

foreach (CalculationHandler calcHandler 
               in allTargets.GetInvocationList())
{
 try { 
   result = calcHandler (4, 3); 
 }
 catch {}

 // We’ve ignored exceptions in this example to let other delegates 
 // do their work. In a real-life scenario you might either let 
 // the exception propagate, or consume it and process it 
 // in a meaningful way.

}

Or… you might want to pass arguments by reference, allow each delegate to modify them as necessary and pass them on.

The entire listing of this sample is available for your perusal.

Compiler Disconnect or…?

Here’s a quote from Jeffrey Richter’s Applied Microsoft .NET Framework Programming (page 375):

When compiling source code, a compiler would check the delegate’s signature and select the more appropriate of the two classes [Delegate or MulticastDelegate] for the compiler-generated delegate type’s base class. For the curious, methods with a signature that indicated a non-void return would be derived from System.Delegate, and methods with a void return value would be derived from System.MulticastDelegate. This made sense because you can get the return value only from the last method called in a linked-list chain.

I fully agree that it would make sense, and the last sentence reiterates the conclusion of this post. However, the code-gen’ed class is clearly derived from MulticastDelegate. I’m not sure what’s going on here and why the compiler chooses this route. In fact, the compiler’s choice of MulticastDelegate as the base class is what gets us in trouble in the first example. Any ideas?

Additional Reading

Again, Jeffrey Richter’s book, Chapter 17 (“Delegates”), pages 371 and on.

Update: Silly me. Should’ve read the rest of the chapter attentively. Jeffrey says Delegate and MulticastDelegate were supposed to be merged to elimibate confusion, but it didn’t happen due to time constraints. Still, it might happen in the future.

Comments

No comments yet

Emails and Notifications

Would you like to be notified when somebody responds to this post? 

Submit your comment

Please enter only text since all HTML tags except hyperlinks will be stripped. Hyperlinks will become live links. Any comments with flaming or offensive language will be deleted. Be courteous to other posters. Thank you.

Your name (required):
Your email (optional):
Your site's URL (optional):
Enter this number
Type in the number above:
Comment (required):