Delegate.CreateDelegate won't box a return value - deliberate, or an ommission?

You can only convert a delegate in this way if the parameters and return value can be converted using a representation conserving conversion.

  • Reference types can only be converted to other reference types in this way
  • Integral values can be converted to other integer values of the same size (int, uint, and enums of the same size are compatible)

A few more relevant blog articles:

This dichotomy motivates yet another classification scheme for conversions (†). We can divide conversions into representation-preserving conversions (B to D) and representation-changing conversions (T to U). (‡) We can think of representation-preserving conversions on reference types as those conversions which preserve the identity of the object. When you cast a B to a D, you’re not doing anything to the existing object; you’re merely verifying that it is actually the type you say it is, and moving on. The identity of the object and the bits which represent the reference stay the same. But when you cast an int to a double, the resulting bits are very different.

This is why covariant and contravariant conversions of interface and delegate types require that all varying type arguments be of reference types. To ensure that a variant reference conversion is always identity-preserving, all of the conversions involving type arguments must also be identity-preserving. The easiest way to ensure that all the non-trivial conversions on type arguments are identity-preserving is to restrict them to be reference conversions. http://blogs.msdn.com/b/ericlippert/archive/2009/03/19/representation-and-identity.aspx

"but how can a value type, like int, which is 32 bits of memory, no more, no less, possibly inherit from object? An object laid out in memory is way bigger than 32 bits; it's got a sync block and a virtual function table and all kinds of stuff in there." Apparently lots of people think that inheritance has something to do with how a value is laid out in memory. But how a value is laid out in memory is an implementation detail, not a contractual obligation of the inheritance relationship! When we say that int inherits from object, what we mean is that if object has a member -- say, ToString -- then int has that member as well. http://ericlippert.com/2011/09/19/inheritance-and-representation/


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

It depends on what the meaning of "is" is, as President Clinton famously said.

For the purposes of covariance, int is not object because int is not assignment compatible with object. A variable of type object expects a particular bit pattern with a particular meaning to be stored in it. A variable of type int expects a particular bit pattern with a particular meaning, but a different meaning than the meaning of a variable of object type.

However, for the purposes of inheritance, an int is an object because every member of object is also a member of int. If you want to invoke a method of object -- ToString, say -- on int, you are guaranteed that you can do so, because an int is a kind of object, and an object has ToString.

It is unfortunate, I agree, that the truth value of "an int is an object" varies depending on whether you mean "is assignment-compatible with" or "is a kind of".

If that covariance can only be achieved via a boxing operation; then the framework should take care of that.

OK. Where? Where should the boxing operation go? Someone, somewhere has to generate a hunk of IL that has a boxing instruction. Are you suggesting that when the framework sees:

Func<int> f1 = ()=>1;
Func<object> f2 = f1;

then the framework should automatically pretend that you said:

Func<object> f2 = ()=>(object)f1();

and thereby generate the boxing instruction?

That's a reasonable feature, but what are the consequences? Func<int> and Func<object> are reference types. If you do f2 = f1 on reference types like this, do you not expect that f2 and f1 have reference identity? Would it not be exceedingly strange for this test case to fail?

f2 = f1;
Debug.Assert(object.ReferenceEquals(f1, f2));

Because if the framework implemented that feature, it would.

Similarly, if you said:

f1 = MyMethod;
f2 = f1;

and you asked the two delegates whether they referred to the same method or not, would it not be exceedingly weird if they referred to different methods?

I think that would be weird. However, the VB designers do not. If you try to pull shenanigans like that in VB, the compiler will not stop you. The VB code generator will generate non-reference-equal delegates for you that refer to different methods. Try it!

Moral of the story: maybe C# is not the language for you. Maybe you prefer a language like VB, where the language is designed to take a "make a guess about what the user probably meant and just make it work" attitude. That's not the attitude of the C# designers. We are more "tell the user when something looks suspiciously wrong and let them figure out how they want to fix it" kind of people.


Even though I think @CodeInChaos is absolutely right, I can't help pointing this Eric Lippert's blog post out. In reply to the last comment to his post (at the very bottom of the page) Eric explains the rationale for such behaviour, and I think this is exactly what you're interested in.

UPDATE: As @Sheepy pointed out Microsoft moved old MSDN blogs into archive and removed all comments. Luckily, the Wayback Machine preserved the blog post in its original form.

Tags:

C#

.Net