Francesco's blog

 Saturday, October 29, 2005

First of all, some background on delegate covariance and contravariance in C# 2.0. Let's suppose you have the following delegate definition:

delegate object GetControlData(TextBox ctrl);

Thanks to delegate covariance, this delegate can point to a method whose return value inherits from the delegate's return type. For example, covariance enables a GetControlData delegate to point to any method that takes a TextBox argument, regardless of its return value, because all .NET types inherit from System.Object. The only condition is that the method actually returns something, therefore the delegate can't point to a void method. For example, you can create a GetControlData delegate that points to the following method, because the String type inherits from Object:

string GetText(TextBox ctrl) 
{ return ctrl.Text; }

Delegate contravariance lets you create a delegate that points to a method whose argument is the base type of the argument that appears in the delegate's signature. In previous example, contravariance enables you to create a GetControlData delegate that points to a method that accepts a Control or Object value, because these types are both base types for the TextBox class specified in the delegate signature. For example, the GetControlData delegate can point to this method:

object GetTag(Control ctrl) 
{ return ctrl.Tag; }

It should be evident that covariance and contravariance don't impact code robustness and can't cause type mismatch errors at runtime. Obviously, covariance and contravariance can be combined, thus a GetControlData delegate can point to the following method:

string GetText(Control ctrl) 
{ return ctrl.Text; }

It's important to notice that - even if the target method accepts a generic Control instance - if you call this method through the delegate, you must pass a TextBox argument, because this is the type that appears in the delegate's signature.

You can find more details on what I've explained so far virtually anywhere on the 'Net, with many more examples. It's time to go back to the main reason for this post.

In case you wondered why I used C# for all the examples, here's the reason: Visual Basic 2005 supports neither covariance nor contravariance.

Is that absolutely correct? Well, yes and no. It's true because VB 2005 doesn't support these feature natively, but you can leverage them all the same. It isn't immediately apparent that covariance and contravariance are supported at the .NET Framework level. In other words, they are a feature of .NET 2.0 delegates, not just C# 2.0. In fact, in .NET 2.0 it is possible to use reflection to create a delegate that has both these properties. Here's how you can proceed if you code with Visual Basic 2005. Say you have the following delegate definition and the following method inside a Windows Form class:

Delegate Function GetControlData(ByVal ctrl As TextBox) As Object

Function GetText(ByVal ctrl As Control) As String
   Return ctrl.Text
End Function

VB 2005 doesn't support covariance and controvariance natively, therefore it isn't possible to create a GetControlData instance that points to the GetText method using pure VB code. However, you can get there anyway via reflection, by creating a MethodInfo object that points to the target method and then passing this MethodInfo object to the Delegate.CreateDelegate static method:

' the target method
Dim targetMethod As MethodInfo = Me.GetType().GetMethod("GetText")
' build the delegate through reflection
Dim deleg As GetControlData = DirectCast([Delegate].CreateDelegate( _
   GetType(GetControlData), Me, targetMethod), GetControlData)
' show that the delegate works correctly
Console.WriteLine(deleg(Me.TextBox1))

This code is only slightly slower than the direct creation of the delegate (that you can implement only in C# 2.0), but this is hardly a serious issue, because you typically create a delegate once and use it many times. Another minor problem is that this code can fail at runtime if the method name is mistyped, but on the other hand you would spot this bug the very first time you run the code.

NOTE: I haven't tested this code against the RTM version yet, but under the RC release I found the .NET support for covariance and contravariance isn't perfect. In fact, not all the overloads of the Delegate.CreateDelegate method support this feature. For example, the overload that takes the name of the target method (instead of the MethodInfo that points to the method) causes a runtime error (ArgumentException: Error binding to target method) if you attempt to create a delegate whose signature doesn't match perfectly the target method's signature.

 
Get RSS/Atom Feed
RSS 2.0 | Atom 1.0
Search in the blog
Archive
<May 2008>
SunMonTueWedThuFriSat
27282930123
45678910
11121314151617
18192021222324
25262728293031
1234567
Categories

Powered by: newtelligence dasBlog 1.8.5223.1