I encountered some unexpected compiler behaviour when calling overloaded method with different Action<T> variations.
Let's say I have this class Test and I'm creating its instance in the CallTest constructor.
public class Test
{
public Test(Action<long> arg)
{
}
public Test(Action<decimal> arg)
{
}
}
public class CallTest
{
public CallTest()
{
Test t = new Test(TestDecimal);
}
public void TestDecimal(decimal arg)
{
}
public void TestLong(long arg)
{
}
}
When calling the Test constructor with either TestDecimal or TestLong as a parameter I'm receiving the following error:
The call is ambiguous between the following methods or properties: 'Test(System.Action<long>)' and 'Test(System.Action<decimal>)'
My guess is there's some implicit conversion going on between long and decimal, but does anyone have any other idea what could have I done wrong? Is there any workaround?
When you pass TestDecimal or TestLong as a parameter you're in fact passing a method group (after all, there could be more than one TestDecimal method - it could have been overloaded). So in both cases implicit conversion occurs - from a method group to a particular delegate type. Both methods are thus applicable candidates (Section 7.4.2). From applicable candidates the overload resolution algorithm tries to find the best candidate. However, the rules of comparing conversions when matching parameter lists state, that if for both candidates implicit conversion occurs neither of them is better:
Section 7.4.2.3:
[...]
Otherwise, neither conversion is better.
That's why in your case there is an ambiguity.
The workaround is of course to first cast the parameter explicitly:
new Test(new Action<decimal>(TestDecimal))
This way for one case there will be no need for implicit conversion during overload resolution (as after the cast Action<T> type will match exactly), and the other would have to be converted (Action<long> to Action<decimal>), and the section mentioned above states that:
[...]
If S is T1, C1 is the better conversion.
If S is T2, C2 is the better conversion.
[...]
There is a workaround:
Test t = new Test(new Action<decimal>(TestDecimal));
This is due to an implicit casting between long and decimal.
Here's a table of implicit castings(for simple types) in C#(Picture Source):
Read more about type conversions here.
Related
A code sample:
interface IFoo { }
class FooImpl : IFoo { }
static void Bar<T>(IEnumerable<T> value)
where T : IFoo
{
}
static void Bar<T>(T source)
where T : IFoo
{
}
Can anybody explain, why this method call:
var value = new FooImpl[0];
Bar(value);
targets Bar<T>(T source) (and, hence, doesn't compile)?
Does compiler take into account type parameter constraints at all, when resolving overloads?
UPD.
To avoid confusion with arrays. This happens with any implementation of IEnumerable<T>, e.g.:
var value = new List<FooImpl>();
UPD 2.
#ken2k mentioned covariance.
But let's forget about FooImpl. This:
var value = new List<IFoo>();
Bar(value);
produces the same error.
I'm sure, that implicit conversion between List<IFoo> and IEnumerable<IFoo> exists, since I can easily write something like this:
static void SomeMethod(IEnumerable<IFoo> sequence) {}
and pass value into it:
SomeMethod(value);
Does compiler take into account type parameter constraints at all, when resolving overloads?
No, because generic constraints are not part of the function signature. You can verify this by adding a Bar overload that is identical except for the generic constraints:
interface IBar { }
static void Bar<T>(IEnumerable<T> value)
where T : IFoo
{
}
static void Bar<T>(T source)
where T : IBar
{
// fails to compile : Type ____ already defines a member called 'Bar' with the same parameter types
}
The reason your code doesn't compile is because the compiler chooses the "best" match based on the method signature, then tries to apply the generic constraints.
One possible reason why it doesn't is because this call would be ambiguous:
{suppose List<T> had an Add<T>(IEnumerable<T> source) method}
List<object> junk = new List<object>();
junk.Add(1); // OK
junk.Add("xyzzy") // OK
junk.Add(new [] {1, 2, 3, 4}); //ambiguous - do you intend to add the _array_ or the _contents_ of the array?
The obvious fix is to use a different name for the Bar method that takes a collection (as is done in the BCL with Add and AddRange.
EDIT: Ok, the reason why Bar<T>(T source) is selected over Bar<T>(IEnumerable<T> source) when passing an List is because of the "7.5.3.2 Better function member" section of the C# language reference. What it says is that when an overload resolution must occur, the arguments types are matched to the parameters types of the applicable function members (section 7.5.3.1) and the better function member is selected by the following set of rules:
• for each argument, the implicit conversion from EX to QX is not better than the implicit conversion from EX to PX, and
• for at least one argument, the conversion from EX to PX is better than the conversion from EX to QX.
(PX being the parameter types of the first method, QX of the second one)
This rule is applied "after expansion and type argument substitution". Since type argument substitution will swap the Bar(T source) to Bar>(IList source), this method arguments will be a better match than the Bar(IEnumerable source) which needs a conversion.
I couldn't find a online version of the language reference, but you can read it here
EDIT: misunderstood the question initially, working on finding the correct answer in the c# language spec. Basically IIRC the method is selected by considering the most appropriate type, and if you don't cast your parameter to IEnumerable<> exactly, then the Bar<T>(T source) will match the parameter type exactly, just like in this sample:
public interface ITest { }
public class Test : ITest { }
private static void Main(string[] args)
{
test(new Test() ); // outputs "anything" because Test is matched to any type T before ITest
Console.ReadLine();
}
public static void test<T>(T anything)
{
Console.WriteLine("anything");
}
public static void test(ITest it)
{
Console.WriteLine("it");
}
Will link to it when found
Because the cast between an array and an enumerable must be explicit: this compiles
var value = new FooImpl[0].AsEnumerable();
Bar(value);
and so does this:
var value = new FooImpl[0] as IEnumerable<IFoo>;
Bar(value);
From the doc:
Starting with the .NET Framework 2.0, the Array class implements the
System.Collections.Generic.IList,
System.Collections.Generic.ICollection, and
System.Collections.Generic.IEnumerable generic interfaces. The
implementations are provided to arrays at run time, and as a result,
the generic interfaces do not appear in the declaration syntax for the
Array class.
So your compiler doesn't know that the array matches the signature for Bar, and you have to explicitly cast it
As of c# 7.3, generic constraints are now considered part of the method signature for the purpose of overload resolution. From What's new in C# 7.0 through C# 7.3: Improved overload candidates:
When a method group contains some generic methods whose type arguments do not satisfy their constraints, these members are removed from the candidate set.
Thus in c# 7.3 / .Net Core 2.x and later, the code shown in the question compiles and runs successfully, and Bar<T>(IEnumerable<T> value) is called as desired.
A demo .Net 5 fiddle here now compiles successfully
A demo .NET Framework 4.7.3 fiddle here still fails to compile with the error:
The type 'FooImpl[]' cannot be used as type parameter 'T' in the generic type or method 'TestClass.Bar<T>(T)'. There is no implicit reference conversion from 'FooImpl[]' to 'IFoo'.
That's a covariance issue. List<T> is not covariant, so there is no implicit conversion between List<FooImpl> and List<IFoo>.
On the other hand, starting from C# 4, IEnumerable<T> now supports covariance, so this works:
var value = Enumerable.Empty<FooImpl>();
Bar(value);
var value = new List<FooImpl>().AsEnumerable();
Bar(value);
var value = new List<FooImpl>();
Bar((IEnumerable<IFoo>)value);
Consider the following code:
public class Tests
{
public void Test()
{
Assert.AreEqual("Int", DoSomething(1));
}
public static string DoSomething<T>(T value)
{
return "Generic";
}
public static string DoSomething(int value)
{
return "Int";
}
}
As expected, the non-generic DoSomething method will be invoked. Now consider the following modification:
public class Tests
{
public void Test()
{
Assert.AreEqual("Int", DoSomething(1));
}
public static string DoSomething<T>(T value)
{
return "Generic";
}
public static string DoSomething<T>(int value)
{
return "Int";
}
}
The only thing I've changed is adding the T type parameter to the second overload, thus making it generic. Note that the type parameter is not used.
That modification causes the first DoSomething method to be called. Why? The compiler has all the information it needs in order to choose the second method.
Can you please explain why, or even better, point me to the section of the C# specification that explains this behavior?
In your call, you're not specifying a type argument - so the compiler would have to infer the type of T. It can't do that for your second method, because the type parameter is never mentioned in the declared parameters. Therefore, that overload is not applicable, and is ignored.
If you specify a type argument to the call, e.g. any of
DoSomething<int>(1)
DoSomething<object>(1)
DoSomething<string>(1)
... then in all cases the second overload will be called.
From section 7.6.5.1 of the C# 5 spec, (method invocations) when constructing the set of candidate methods:
If F is generic and M has no type argument list, F is a candidate when:
Type inference (§7.5.2) succeeds, inferring a list of type arguments for the call, and
Once the inferred type arguments are substituted for the corresponding method type parameters, all constructed types in the parameter list of F satisfy their constraints (§4.4.4), and the parameter list of F is applicable with respect to A (§7.5.3.1).
As type inference doesn't succeed, the second method isn't in the candidate set, so by the time we get to real overload resolution, the set just has a single method in (the first one).
the compiler can not determine the type of pattern in the call DoSomething(1), but if you specify [int] it will be selected another method.
Why is the following call ambiguous:
public class Foo
{
public void Bar<T> (Action<T> simple);
public void Bar<T1, T2> (Action<T1, T2> complex);
}
...
public class Test
{
public static void MyComplex (string a, string b) { ... }
}
...
foo.Bar(Test.MyComplex);
Shouldn't it be clear to the compiler to call the Bar<T1,T2>() method?
If you remove this method public void Bar<T> (Action<T> simple);, your code just will not compile, because you get this exception:
The type arguments for method 'Foo.Bar(System.Action)'
cannot be inferred from the usage. Try specifying the type arguments
explicitly.
Unfortunately the compiler can not get types from this method, and you should write this code to call method:
new Foo().Bar(new Action<string, string>(Test.MyComplex));
The compiler is trying to infer the generic parameter types for Bar but to do that it needs to know all the argument types. The argument you have (Test.MyComplex) is actually method group, not a delegate so the compiler is also required to insert a conversion to a compatible delegate type. However it can't because it does not know the what delegate types to use because type inference on the method it needs to be compatible with has not completed yet. There's a chicken-and-egg problem and the compiler gives up saying the call is ambiguous. Eric Lippert points out in the comments to a very similar question that in simple cases like this, it could be worked out but at the expense of complicating the overload resolution rules.
Unfortunately you are required to do something that gives the compiler more information:
foo.Bar<string, string>(Test.MyComplex);
or
Action<string, string> action = Test.MyComplex;
foo.Bar(action);
class GenericWrapper<T>
{
}
class WrapperInstance : GenericWrapper<string>
{
}
class Usage
{
public static Usage Create<T1, T2> (T2 t2) where T1 : GenericWrapper<T2>
{
return null;
}
}
...
// works
Usage.Create<WrapperInstance, string>("bar");
// doesnt work
Usage.Create<WrapperInstance>("bar");
I suspect the answer is no, but is there a way I can make the last line compile?
I want the compiler to force me to provide a string argument without having to know or first go and examine WrapperInstance to see what T of GenericWrapper it implements.
I know I can make it compile by either using the first method or by taking object as the argument and doing runtime checking, but thats not the question ;) I largely suspect these are my only two options.
Thanks
I suspect the answer is no, but is there a way I can make the last line compile?
No. Create has two generic type parameters. You either specify none or you specify both. In the case of none, the compiler will try to infer the types from the invocation arguments. However, in this case it can not because T1 never appears in the argument list. Therefore you must specify both.
There are two problems here:
You want to infer just one type argument, and specify the other. You can't do that with normal type inference. However, you could make Usage generic, thus specifying one type argument there, and letting the other be inferred using the generic method:
Usage<WrapperInstance>.Create("foo");
That's something I've often done before, but that just leads to the second problem...
The type parameter you want to specify (T1) is constrained by the one you want to infer (T2). The above example can't do that, as Usage<WrapperInstance> doesn't "have" a T2 to validate... and you can't constrain an existing type parameter on a generic method - only ones which are introduced in the method.
There's one way I think we could do this:
public class Usage
{
public static Usage<T2> For<T2>(T2 t2)
{
return new Usage<T2>(t2);
}
}
public class Usage<T2>
{
private readonly T2 t2; // Assuming we need it
public Usage(T2 t2)
{
this.t2 = t2;
}
// I don't know what return type you really want here
public static Foo Create<T1>() where T1 : GenericWrapper<T2>
{
// Whatever
}
}
You'd use it like this:
Usage.Foo("bar").Create<WrapperInstance>();
Without knowing more about what you're trying to do, I don't know whether or not that's helpful - but it does manage to accomplish what you were after in terms of:
Validating the WrapperInstance type argument
Inferring the string type argument
I have the following C# class:
public class MyType<T>
{
public void TryParse(string p_value)
{
T value ;
Parser.TryParse(p_value, out value);
// Do something with value
}
}
The point is to call the right Parser.TryParse method, depending on the generic type T.
This uses the following static class:
static public class Parser
{
static public void TryParse(string p_intput, out object p_output)
{
// Do something and return the right value
}
static public void TryParse(string p_intput, out double p_output)
{
// Do something and return the right value
}
static public void TryParse(string p_intput, out int p_output)
{
// Do something and return the right value
}
}
I expected this to work: In the worst case, the "object" TryParse would be called. Instead, I have two compilation errors:
CS1502: The best overloaded method match for 'Parser.TryParse(string, out object)' has some invalid arguments
CS1503: Argument 2: cannot convert from 'out T' to 'out object'
Question 1: I don't understand why this doesn't work: I can be naive, but aren't all C# objects supposed to derive from "object" ? Why T cannot be converted to object?
Question 2: How can I dispatch a method with generic type T into the right non-generic methods (i.e. MyType<T>.TryParse calling the right Parser.TryParse according to the right type of T) ?
Note
The question was edited to reflect the original question intent (as written in the title: How to dispatch C# generic method call into specialized method calls)
Actually, ref and out parameters do not allow type variation. So, to pass a variable to a method expecting an out object parameter, that variable must be declared as object.
From the specification (§10.6.1.2 and §10.6.1.3)
When a formal parameter is a reference parameter, the corresponding argument in a method invocation must consist of the keyword ref followed by a variable-reference (§5.3.3) of the same type as the formal parameter.
When a formal parameter is an output parameter, the corresponding argument in a method invocation must consist of the keyword out followed by a variable-reference (§5.3.3) of the same type as the formal parameter.
See: Why do ref and out parameters not allow type variation? for some insight into why.
Bonus question: How can I dispatch a method with generic type T into the right non-generic methods (i.e. MyType<T>.TryParse calling the right Parser.TryParse according to the right type of T)?
I'm going to turn it back around on you. Why are you doing this? If you are invoking MyType<T>.TryParse as, say, MyType<int>.TryParse, why not call Int32.TryParse directly? What is this extra layer buying you?
I know this is somewhat low-tech, but I have had the same problem, where I solved it by making a Dictionary<Type, Parser> containing the individual parsers. I will be interested in what answers this questions bring.
Regards,
Morten
Current solution
The current solution I use at work is based on dynamic dispatch, that is, the keyword dynamic as defined on C# 4.0.
The code is something like (from memory) :
public class Parser
{
static public void TryParse<T>(string p_input, out T p_output)
{
// Because m_p is dynamic, the function to be called will
// be resolved at runtime, after T is known...
m_p.DoTryParse(p_input, out p_output) ;
}
// The dynamic keyword means every function called through
// m_p will be resolved at runtime, at the moment of the call
private dynamic m_p = new Parser() ;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
private void DoTryParse(string p_input, out double p_output)
{ /* Do something and return the right value */ }
private void DoTryParse(string p_input, out int p_output)
{ /* Do something and return the right value */ }
// etc.
private void DoTryParse<T>(string p_input, out T p_output)
{
// fallback method... There are no dedicated method for T,
// so p_output becomes the default value for T
p_output = default(T) ;
}
}
The elegant part is that it can't fail (the fallback function will be called, if none with a better signature match is found), and that it follows a simple pattern (overload the function).
Of course, the real-life, production code is somewhat different, and more complicated because, with but one public static method, I want to :
parse both reference objects (classes) and value objects (structs)
parse enums
parse nullable types
I want to offer the user the possibility to derive from Parser to offer its own overloads in addition to the default ones
But I guess the use of dynamic in the current solution is, in the end, the same thing as doing reflection as done in the original answer below. Only the "notation" changes.
Conclusion, I now have the following method :
public class Parser
{
static public void TryParse<T>(string p_input, out T p_output)
{
// etc.
}
}
which is able to parse anything, including in situations where T is not known at compile time (because the code is generic).
Original answer
Jason's answer was right about the first question (about the compiler errors). Still, I had no solution to my problem (dispatching from a generic method to non-generic methods according to the runtime generic type T).
I tried LukeH's answer, but it didn't work: The generic method is always called, no matter what (even when removing the out qualifier of the second parameter).
Morten's answer is the most sane one that should works, but it doesn't make use of reflection.
So, to solve my own problem, I used reflection. This needs the rewriting of the generic TryParse method:
public class MyType<T>
{
public void TryParse(string p_value)
{
T value = default(T);
// search for the method using reflection
System.Reflection.MethodInfo methodInfo = typeof(Parser).GetMethod
(
"TryParse",
new System.Type[] { typeof(string), typeof(T).MakeByRefType() }
);
if (methodInfo != null)
{
// the method does exist, so we can now call it
var parameters = new object[] { p_value, value };
methodInfo.Invoke(null, parameters);
value = (T)parameters[1];
}
else
{
// The method does not exist. Handle that case
}
}
}
I have the source code available if needed.
This problem intrigued me, so I did some research and found a nice thing by Paul Madox. This seems to do the trick.
public static T SafeParseAndAssign<T>(string val) where T: new()
{
try
{
T ValOut = new T();
MethodInfo MI = ValOut.GetType().
GetMethod("Parse", new Type[] { val.GetType() });
return (T)MI.Invoke(ValOut, new object[] { val });
}
catch
{
// swallow exception
}
return default(T);
}