Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Conditional statement, generic delegate unnecessary cast

I'm having this really weird problem with a conditional statement when setting an Action<T> value. It's not that I don't know how to work around this as it's pretty easy to solve by using a normal if.

Here's my problem:

public class Test
{
    public bool Foo { get; set; }
    public Action<bool> Action { get; set; }

    public void A()
    {
        Action = Foo ? B : C;//Gives compiler error
    }

    public void B(bool value)
    {

    }

    public void C(bool value)
    {

    }
}

This gives me a compiler error with the message

There's no implicit conversion between 'method group' and 'method group'.

Which is strange as I can't figure out why this would be illegal.

By the way, the below syntax will make this valid (from the compilers point of view):

    public void A()
    {
        Action = Foo ? (Action<bool>) B : C;
    }

So maybe you can read the question as, why is the cast necessary?

like image 230
thekip Avatar asked Aug 16 '11 14:08

thekip


2 Answers

You're conflating two similar concepts:

A) A method group. A method group is one or more C# methods with the same name. It's an abstraction used primarily by the compiler; you can't pass around a method group. All you can do with a method group is invoke it or create a delegate out of it. You can implicitly create a delegate from a method group if the type signatures match.

B) A delegate. You know what a delegate is; it has a specific type signature and refers directly to a method. As well as invoking it, you can pass it around and treat it as a first-class object.

So in the first example, your expression returns a method group B on the one side and another method group C on the other side. The ternary operator needs to return the same type on both sides, but it doesn't know what to cast either side to; the variable type you assigned the result to (Action<bool>) doesn't determine the type of the expression. So it's ambiguous.

In the second example, you legally cast the method group B to an Action<bool> delegate on the one side of the ternary operator. In the process of trying to disambiguate the expression, the compiler tries to cast each side to the type of the other side. It can successfully cast method group C to an Action<bool>, so it does so and the expression is legal.

like image 168
mqp Avatar answered Nov 07 '22 23:11

mqp


Because B and C aren't actually delegates. They're method groups, and they can be implictly converted to delegates (in particular Action<bool>), but that's not the same thing.

The type of the conditional expression must be consistent on both branches, and since B and C are currently method groups (which are not typed,) the compiler can't figure out what the type should be. As it tells you, there's no implicit conversion between them.

As well, it can't (or at least doesn't) look over to the other side of the assignment operator and say "oh, it should be Action<bool>".

When you add a cast, the type of the left branch expression becomes Action<bool>, and there is an implicit conversion between the method group on the other side and that delegate, so the compiler is happy again: the type of the entire expression is Action<bool>.

like image 33
dlev Avatar answered Nov 07 '22 23:11

dlev