可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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?
回答1:
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.
回答2:
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>
.
回答3:
I think, Eric will again tell me, that my reasoning is slightly incorrect, but I will give it a try anyway and hope for his correction :-)
A method group, for example B
, doesn't have a type, it isn't an object (B.GetType()
isn't going to compile).
It can easily be converted into a type, that's why an implicit cast exists.
Sample:
Action<bool> a = B; // implicit cast taking place.
However, as you can see in the linked question, the ternary expression tries to find a return type that both parts of the expression match. It doesn't know that later a conversion to Action<bool>
should happen. Because method groups are no types per se, there exists no conversion between them and B
can't be converted to C
and thus, the compiler complains just about that.
By casting any of the parts of the ternary expression to Action<bool>
, you tell the compiler, that the return type should be that type and it checks whether the other part of the ternary expression supports an implicit cast to that type. Because this is the case, the code will compile.
回答4:
An Action is a specific delegate class, and there isnt an impicit conversion available from/to a regular delegate with a similar signature.