Saturday 25 August 2012

Avoid creating Custom Delegate Types

  • I used to think about custom delegate types as something useful due to how they can convey semantics, that's why I used to create delegate types like "DrawingStrategy", "DateFormatter" and so on. Unfortunately the .Net Framework BCL with its huge number of delegate types (ThreadStartDelegate, TimerCallback...) is a good source of inspiration to follow this wrong pattern. Why I'm saying this is wrong?, cause contrary to what we could expect, there's not any kind of automatic conversion between different Delegate types which signature is exactly the same, I mean, if we have 2 delegate types like these:

    delegate bool FormatValidator(string st);

    delegate bool OrtographyValidator(string st);

    though the same method can be wrapped in any of them, once the delegate is constructed we can't pass a FormatValidator to a method expecting an OrtographyValidator, and viceversa

    This means that you should avoid custom delegates types and use the generic delegates: Action, Action<T1>... and Function<TR>, Function<T1, TR>...

    As stated above, the BCL is staffed with custom delegates types, so more often than not you'll be faced with a situation where you'll need to obtain a WhateverFancyDelegateName1 delegate from another WhateverFancyDelegateName2 delegate. You have two options, either use a lambda/Anonymous method to wrap DelegateName2 into a DelegateName1, or use the slower, Delegate.CreateDelegate. I could spend time writing sample code here, but this excellent post does just that, so no need to rewrite what is already properly put there.

    I think a good source of confusion that could wrongly draw us to think that the compiler would accept any delegate type when the signatures match, is the always complex topic of Covariance and Contravariance, so let's review a bit how delegate variance works:

    • C# 2.0 added delegate return type covariance and delegate parameter type contravariance. Indeed, this has nothing to do with the issua at hand, as it's not dealing with different delegate types, but with what methods can be used for a specific delegate type. We can see a sample here
    • C# 4.0 added Variance for Generic delegates. This is a bit more related to our case, as in a sense we're talking about differente Delegate types, but only a subset of them.

    I've put together this sample with the different kinds of delegate variance that is supported. To my surprise, it fails to compile under mono (both on Windows and Linux)!.

  • No comments:

    Post a Comment