Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implicit cast of Func<MyType> to MyType

Given the following class:

public class MyType
{
    public static implicit operator MyType(Func<MyType> wrapper) {
        return wrapper();
    }
}

From the implicit cast of Func<MyType> to MyType, I assumed the following would be possible:

public MyType MyTypeWrapper() {
    return new MyType();
}

public void MyTestMethod() {
    MyType m = MyTypeWrapper; // not a call!
}

However I'm getting:

Cannot convert method group 'MyTypeWrapper' to non-delegate type 'Test.MyType'. Did you intend to invoke the method?

Which, unfortunately for me, when searched for (as I half expected) resulted in tons of questions to which the answer was:

Hey, ya dun goofed; toss () on the end of WhateverMethod!

Now, as I'm typing this, I've noticed that an explicit cast does in fact compile:

MyType m = (MyType) MyTypeWrapper;

Why is it that I cannot implicitly cast a Func<MyType> to MyType as I've described?

like image 517
Dan Lugg Avatar asked Jun 14 '13 19:06

Dan Lugg


4 Answers

This is unfortunate. I'm pretty sure you've found a compiler bug, and this section of the specification is extremely difficult to read.

Section 6.4.4 of the C# 4 specification explains why your implicit conversion is illegal.

The algorithm goes like this. First look at the source type and target type. There is no source type because a method group has no type. The target type is MyType. So search MyType for user-defined implicit conversions. Now the question is: what is the set of applicable user-defined operators ... that convert from a type encompassing S? S is the source type and we have already established that there is no source type. So this is already evidence that the conversion should fail. But even if the compiler for some reason decides that your Func<MyType> conversion is applicable, the rule is a standard implicit conversion ... is performed. Method group conversions are deliberately not classified as standard conversions.

So that's why it should be illegal.

Why then is the explicit cast legal?

There's no justification for that. This appears to be a bug.

This is unfortunate; many apologies for the error. I shall report it to my former colleagues; if they have an analysis which conflicts with mine, I'll update the answer.

UPDATE: My former colleagues inform me that the spec problem whereby the source expression is assumed to have a type will be addressed by a rewording in the next release of the spec. No word yet as to whether the explicit cast behavior is a bug.

like image 152
Eric Lippert Avatar answered Nov 12 '22 14:11

Eric Lippert


You're already using the built-in implicit conversion from method group to Func<MyType>.

The compiler won't do two implicit conversions at once.

Once you have an explicit cast to your class, the compiler knows to look for an implicit cast to any type that can be explicitly casted to your class.

like image 27
SLaks Avatar answered Nov 12 '22 14:11

SLaks


Because the C# compiler isn't able to convert MyTypeWrapper into a Func<MyType>(MyTypeWrapper). There's a difference between a method group and an actual delegate.

This compiles and runs fine:

MyType m = new Func<MyType>(MyTypeWrapper);

There is an implicit conversion from a method group to a delegate type that matches that group, and there is your user defined implicit conversion from that delegate to a type. The general idea here is that the compiler is only going to use one implicit conversion in a row at a time. When it has an A and needs a C it looks for conversions from A to C, not from A to any type B and from that type to C. That algorithm goes from O(n) to O(n^2) (not to mention possibly being quite confusing for programmers).

The reason your code works when using an explicit cast to MyType is that you're no longer chaining implicit conversions.

like image 1
Servy Avatar answered Nov 12 '22 16:11

Servy


The signature of MyTestMethod MATCHES the signature of Func<MyType> but is NOT a Func<MyType>. Func has defined some implicit casts itself to allow you to assign such methods as Funcs, but you must explicitly cast for the signature to apply, because the compiler will not chain implicit casts together for you:

MyType m = (Func<MyType>)MyTypeWrapper; // not a call!
like image 1
Haney Avatar answered Nov 12 '22 15:11

Haney