问题
Suppose I had this in C#:
class OverloadTest
{
void Main()
{
CallWithDelegate(SomeOverloadedMethod);
}
delegate void SomeDelegateWithoutParameters();
delegate void SomeDelegateWithParameter(int n);
void CallWithDelegate(SomeDelegateWithoutParameters del) { }
void CallWithDelegate(SomeDelegateWithParameter del) { }
void SomeOverloadedMethod() { }
void SomeOverloadedMethod(int n) { }
}
Of course, this does not compile, because the line CallWithDelegate(SomeOverloadedMethod);
is ambiguous.
Now, suppose there was only one CallWithDelegate(SomeDelegateWithoutParameter del)
function (no overloads). In this case, there would be no ambiguity, because, from what seems to be happening, the compiler can look at the parameter type and discard SomeOverloadedMethod(int n)
from the candidate list (since it can only take a SomeDelegateWithoutParameters
), and so it compiles.
I don't intend to write code like this; this is just out of curiosity, from a compiler writer point-of-view. I couldn't find an answer about this, since it is quite confusing to put into words.
I'd like to know if there is any way in C# to disambiguate that call in Main()
in the example given, so that it would compile. How can you specify it so that it resolves into CallWithDelegate(SomeDelegateWithoutParameters del)
being passed SomeOverloadedMethod()
, or CallWithDelegate(SomeDelegateWithParameter del)
being passed SomeOverloadedMethod(int n)
?
回答1:
There are several ways to disambiguate overload resolution of method groups.
Method 1: cast the method group
CallWithDelegate((SomeDelegateWithoutParameters)SomeOverloadedMethod);
CallWithDelegate((SomeDelegateWithParameter)SomeOverloadedMethod);
This disambiguates the overload. That's pretty uncommon syntax in the wild, but it works (C# 5 spec §6.6 Method group conversions):
As with all other implicit and explicit conversions, the cast operator can be used to explicitly perform a method group conversion.
[...]
Method groups may influence overload resolution, and participate in type inference.
Method 2: instantiate the delegate explicitly
CallWithDelegate(new SomeDelegateWithoutParameters(SomeOverloadedMethod));
CallWithDelegate(new SomeDelegateWithParameter(SomeOverloadedMethod));
This is the same as the previous method without the syntactic sugar. See the spec at §7.6.10.5 Delegate creation expressions for more details.
The binding-time processing of a delegate-creation-expression of the form
new D(E)
, whereD
is a delegate-type andE
is an expression, consists of the following steps:
- If
E
is a method group, the delegate creation expression is processed in the same way as a method group conversion (§6.6) fromE
toD
.[...]
There's even an example closely related to your question:
As described above, when a delegate is created from a method group, the formal parameter list and return type of the delegate determine which of the overloaded methods to select. In the example
delegate double DoubleFunc(double x); class A { DoubleFunc f = new DoubleFunc(Square); static float Square(float x) { return x * x; } static double Square(double x) { return x * x; } }
the
A.f
field is initialized with a delegate that refers to the secondSquare
method because that method exactly matches the formal parameter list and return type ofDoubleFunc
. Had the secondSquare
method not been present, a compile-time error would have occurred.
Method 3: use a lambda
CallWithDelegate(() => SomeOverloadedMethod());
CallWithDelegate(i => SomeOverloadedMethod(i));
CallWithDelegate((int i) => SomeOverloadedMethod(i)); // Explicit types, if needed
This form is not ambiguous but it has an indirection (the lambda is called, and it then calls the target method). This may get optimized by the JIT though, and it most probably won't have a visible performance impact anyway.
Method 4: use anonymous delegates
CallWithDelegate(delegate() { SomeOverloadedMethod(); });
CallWithDelegate(delegate(int i) { SomeOverloadedMethod(i); });
This is equivalent to the lambda calls, but it uses the bulkier (and older) delegate
syntax.
If you'd like to know the exact overload resolution rules, they're described in the spec in §7.5.3 Overload resolution.
回答2:
While i am no compiler expert, I can tell you that since you have supplied 2 overloaded methods that match both methods the compiler has no way of identifying your actual intention. It can't compile because at compile time there is currently no actual identifying information as now mentioned by Lucas you can cast to remove the ambiguity. For the compiler to solve this it would need run-time information of which method you actually wanted to use based on arguments that you may try and pass in.
来源:https://stackoverflow.com/questions/31866090/disambiguating-between-overloaded-methods-passed-as-delegates-in-an-overloaded-c