I have a static method:
public class Example
{
//for demonstration purposes - just returns default(T)
public static T Foo<T>() { return default(T); }
}
And I need to be able to invoke it using a Type parameter calls to which could be numerous, so my standard pattern is to create a thread-safe cache of delegates (using ConcurrentDictionary in .Net 4) which dynamically invoke the Foo<T> method with the correct T. Without the caching, though, the code is this:
static object LateFoo(Type t)
{
//creates the delegate and invokes it in one go
return (Func<object>)Delegate.CreateDelegate(
typeof(Func<object>),
typeof(Example).GetMethod("Foo", BindingFlags.Public | BindingFlags.Static).
MakeGenericMethod(t))();
}
This is not the first time I’ve had to do this – and in the past I have use Expression trees to build and compile a proxy to invoke the target method – to ensure that return type conversion and boxing from int -> object (for example) is handled correctly.
Update – example of Expression code that works
static object LateFoo(Type t)
{
var method = typeof(Example)
.GetMethod("Foo", BindingFlags.Public | BindingFlags.Static)
.MakeGenericMethod(t);
//in practise I cache the delegate, invoking it freshly built or from the cache
return Expression.Lambda<Func<IField, object>>(Expression.Convert(
Expression.Call(method), typeof(object))).Compile()();
}
What’s slightly amusing is that I learned early on with expressions that an explicit Convert was required and accepted it – and in lieu of the answers here it does now make sense why the .Net framework doesn’t automatically stick the equivalent in.
End update
However, this time I thought I’d just use Delegate.CreateDelegate as it makes great play of the fact that (from MSDN):
Similarly, the return type of a delegate is compatible with the return type of a method if the return type of the method is more restrictive than the return type of the delegate, because this guarantees that the return value of the method can be cast safely to the return type of the delegate.
Now – if I pass typeof(string) to LateFoo method, everything is fine.
If, however, I pass typeof(int) I get an ArgumentException on the CreateDelegate call, message: Error binding to target method. There is no inner exception or further information.
So it would seem that, for method binding purposes, object is not considered more restrictive than int. Obviously, this must be to do with boxing being a different operation than a simple type conversion and value types not being treated as covariant to object in the .Net framework; despite the actual type relationship at runtime.
The C# compiler seems to agree with this (just shortest way I can model the error, ignore what the code would do):
public static int Foo()
{
Func<object> f = new Func<object>(Foo);
return 0;
}
Does not compile because the Foo method ‘has the wrong return type’ – given the CreateDelegate problem, C# is simply following .Net’s lead.
It seems to me that .Net is inconsistent in it’s treatment of covariance – either a value type is an object or it’s not; & if it’s not it should not expose object as a base (despite how much more difficult it would make our lives). Since it does expose object as a base (or is it only the language that does that?), then according to logic a value type should be covariant to object (or whichever way around you’re supposed to say it) making this delegate bind correctly. If that covariance can only be achieved via a boxing operation; then the framework should take care of that.
I dare say the answer here will be that CreateDelegate doesn’t say that it will treat a box operation in covariance because it only uses the word ‘cast’. I also expect there are whole treatises on the wider subject of value types and object covariance, and I’m shouting about a long-defunct and settled subject. I think there’s something I either don’t understand or have missed, though – so please enlighten!
If this is unanswerable – I’m happy to delete.
You can only convert a delegate in this way if the parameters and return value can be converted using a representation conserving conversion.
A few more relevant blog articles: