Why is the generic method called when both overloads would match?
public static void method1(object obj)
{
Console.WriteLine("Object");
}
public static void method1<T>(T t)
{
Console.WriteLine("Type T");
}
public static void Main(String args[])
{
method1("xyz"); //Will print "Type T";
}
There should not be any conflicts here, right?
Overloads are resolved by choosing the most specific overload. In this case, method1<string>(string) is more specific than method1(object) so that is the overload chosen.
There are details in section 7.4.2 of the C# specification.
If you want to select a specific overload, you can do so by explicitly casting the parameters to the types that you want. The following will call the method1(object) overload instead of the generic one:
method1((object)"xyz");
There are cases where the compiler won't know which overload to select, for example:
void method2(string x, object y);
void method2(object x, string y);
method2("xyz", "abc");
In this case the compiler doesn't know which overload to pick, because neither overload is clearly better than the other (it doesn't know which string to implicitly downcast to object). So it will emit a compiler error.
C# will always choose the most specific method it can.
When compiling
method1("xyz");
it will look for all methods with the specified name and then attempt to match parameters. The compiler will choose the method that is the most specific, in this case it would prefer
method1(string s)
over
method1<T>(T t) with T = string
and lastly
method1(object o)
Please note #Erik's excellent answer for an example where the compiler fails to decide.
Because you are already passing in T as a parameter so you don't need to type out method1<string>("xyz");you can just go method1("xyz");, .Net already knows it's a string. If you had method1 then it would be a different story.
Also since method1(object obj) doesn't take in a string as parameter it will favor the generic function first where it can infer T. If you were to change method1(object obj) to method1(string obj) it would favor it first then the generic.
How method overloading works
To find the matching signature of a method for a call, the compiler search in the type hierarchy from bottom to top as well in virtual table:
First in the class hierarchy,
Then in the interface hierarchy.
Because classes prevail on interfaces.
Indeed, before being of type of an interface, an object is of type of a class first of all.
And non generic signatures prevail over generic as reality and facts prevail over abstraction, unless using the generic parameter allow a call on the more specialized type of instance.
Applying the theory to the question
This call:
method1("xyz");
Match perfectly with:
void method1<T>(T t) { }
Before matching with:
void method1(object obj)
Because string is a specialized object and it can be used as a generic parameter to be more acurate.
On the other side, if you write:
void method1(string obj) { }
void method1<T>(T t) { }
The first method is so called.
Case study
var instance = new List<string>();
MyMethod(instance);
MyMethod((IEnumerable<string>) instance);
MyMethod<string>(instance);
MyMethod((object)instance);
void MyMethod<T>(List<T> instance) { }
void MyMethod<T>(IEnumerable<T> list) { }
void MyMethod<T>(T instance) { }
void MyMethod(object instance) { }
The first call calls the first method because instance is type of List (type matching).
The second call calls the second method because of the side cast (implementation).
The third call calls the third method because of the generic parameter specified to act on (templating).
The fourth call calls the fourth method because of the down cast (polymorphism).
Related
The problem:
public class Test
{
public void A<T>(T arg)
{
// We have proof it's safe to call B
if (arg is IEquatable<T>)
{
// To call B, something like this needed.
// var arg1 = (T:IEquatable<T>) arg;
// B(arg1)
B(arg); // Error CS0314: The type 'T' cannot be used as type parameter 'T' in the generic type or method 'Test.B<T>(T)'.
// There is no boxing conversion or type parameter conversion from 'T' to 'System.IEquatable<T>'.
}
else
{
C(arg);
}
}
public void B<T>(T arg)
where T: IEquatable<T>
{
// We don't want to loos the original type T that has been passed to A<T>
Console.WriteLine(typeof(T).Name);
}
public void C<T>(T arg)
{
}
}
Do we have any C# language constructs that allows to call more generic method with more restrictions on type, if we have proofs it's safe (Higher Kinder Polymorphism, advanced pattern matching, etc) ?
Do we have any option to make a weird unsafe hack to make unallowed generic method call ?
Looks like we can express the call on IL level (Fody + https://github.com/ltrzesniewski/InlineIL.Fody) or somehow Function Pointers can be used.
(Sure reflection can help, but it's expensive and might cause an issues with the CoreRT native compilation).
Instead of using a type constraint what if your parameter type would be IEquatable<T>?
You don't know anything else about that T type so it isn't worth using it as the parameter type instead of IEquatable<T> type.
class Test
{
public void A<T>(T arg)
{
if (arg is IEquatable<T> equatable)
{
B1(equatable);
}
else
{
C(arg);
}
B1(2);
B2(2);
}
public void B1<T>(IEquatable<T> arg)
{
// This should write the original type T that has been passed to A<T>
// because if a type implements IEquatable<>
// then the type parameter should be the same as the implementing type.
// Just what your type constraint expects, anyway.
Console.WriteLine(typeof(T).FullName);
// But you can also get the real T type by using .GetType() method.
Console.WriteLine(arg.GetType());
// Should output true.
Console.WriteLine(typeof(T) == arg.GetType());
}
// You can keep this method as an overload,
// if you really need it for some reason.
public void B2<T>(T arg) where T : IEquatable<T>
{
B1(arg);
}
public void C<T>(T arg)
{
}
}
To call B, something like this needed.
var arg1 = (T:IEquatable<T>) arg
Yes, that is the core of the problem. And no, such a language feature doesn't exist.
To call B1 as is, you need to structure you solution so that the caller can provide a type that matches its signature. The only alternative is reflection.
So your choices are either to relax the constraints of B1. Perhaps by moving the implementation to a new private method which is easier to call from A. Or overload A<T> so the caller can prove that T:IEquatable<T>.
Why does the first call to Foo below compile but the second one results in an ambiguous invocation compiler error?
(using c# 7.2)
private static void AmbiguousAsyncOverload() {
Foo(() => Bar()); // This is OK
//Foo(Bar); // Error, ambiguous overload
}
private static void Foo(Func<int> func) {
func();
}
private static void Foo(Func<string> func) {
func();
}
private static int Bar() {
return 4;
}
If I remove the first (Func<int>) implementation of Foo, and hence the possibility of ambiguity, then the compiler (correctly) reports that Bar doesn't have the correct signature to be passed into Foo, which implies it has enough information to resolve the ambiguity.
I would understand if the compiler didn't look at return values during overload resolution and therefore both calls failed, but my question is why does one call compile ok while the other doesn't.
This was an issue for all versions of C# up until it was fixed in v7.3. Return types were not taken into account during overload resolution. From the release notes (or the language proposal) for C# 7.3:
For a method group conversion, candidate methods whose return type doesn't match up with the delegate's return type are removed from the set.
I've been playing a little with generics and delegates and I have found something I don't understand. I have quite similar generic static methods, one accepts Action<T> and the second one accepts Func<T>. Now the problem: if I call the one accepting Func<T> without explicit Type, compiler is fine with that. But with the one accepting Action<T> my program can't be compiled (see the code for error message).
My question is: Why is compiler able to recognize return type, but is not able to recognize argument type?
public interface IMessage
{ }
public class Message : IMessage
{
}
static void HandleAction<TMessage>(Action<TMessage> action)
where TMessage : IMessage
{ }
static void HandleFunction<TMessage>(Func<TMessage> action)
where TMessage : IMessage
{ }
static void A(Message message)
{ }
static Message F()
{
return new Message();
}
static void Main(string[] args)
{
// this one is ok
HandleFunction(F);
// compiler error:
// The type arguments for method
// 'template_test.Program.HandleAction<TMessage>(System.Action<TMessage>)'
// cannot be inferred from the usage.
//Try specifying the type arguments explicitly.
//HandleAction(A);
// this one is ok
HandleAction<Message>(A);
}
I'm using .NET 4.5 in Visual Studio 2012.
Methods can be overloaded by their arguments and all overloads form one method group, so for example void Xyz(int i) and void Xyz(string s) are within same method group called Xyz. Compiler is not able to deduct a type of argument even if user defines only one method, because behaviour of compiler is quite strict.
Methods can't be overloaded by return types, so you can't have int Xyz() and string Xyz() within same class. Return type can be deducted by compiler easily, because there is no overloading.
It was not obvious for me for the first time, but it has been quite clear after I realized that I could create an overload.
Ok I must have got the title terribly wrong. More code, less words:
public class Manager<T> where T : Control, new()
{
void Manage()
{
Do(t => IsVisible(t));
}
bool IsVisible(T t)
{
return t.Visible;
}
S Do<S>(Func<T, S> operation)
{
return operation(new T());
}
}
The compiler is happy about Do. It can infer the T type easily. Now lets say I have this:
public static class Util
{
static void Manage()
{
Do(t => IsVisible(t)); //wiggly line here
}
static bool IsVisible<T>(T t) where T : Control
{
return t.Visible;
}
static S Do<S, T>(Func<T, S> operation) where T : Control, new()
{
return operation(new T());
}
}
The compiler wants types to be explicitly typed now. Here is what I think:
In the first class T was easily inferable from IsVisible method which had T overload and T is known all over the Manager class, no big deal. But in the second case T is specified as a generic constraint on the method and may be that's harder to infer. Ok.
But this doesn't work either:
public static class Util
{
static void Manage()
{
Do(t => IsVisible(t)); //still wiggly line
}
static bool IsVisible(Control t)
{
return t.Visible;
}
static S Do<S, T>(Func<T, S> operation) where T : Control, new()
{
return operation(new T());
}
}
Why doesn't compiler infer T in the last case?
More importantly, how different is the last case from the first? In the first case compiler have to infer it from IsVisible method and then go all the way back to check what T is in the class containing IsVisible, where as in the last case its readily available in IsVisible method. So I assume third case is easier than the first.
(First case)
The compiler is happy about Do. It can infer the T type easily.
It's not inferring T at all. T is a type parameter for the class:
public class Manager<T> where T : Control, new()
(Second case)
The compiler wants types to be explicitly typed now.
I assume you mean in the Manage code:
Do(t => IsVisible(t))
And that's right. What do you think the type of T should be here? How would you expect the complier to infer it?
(Third case, where the method is IsVisible(Control t))
Why doesn't compiler infer T in the last case?
It can't do so from just the parameters. It sounds like you're expecting it to work out every type for which the body of the lambda expression could work... and type inference simply doesn't work that way. You can easily give the compiler enough information though:
Do((Control t) => IsVisible(t));
More importantly, how different is the last case from the first?
In the first case, T isn't a type parameter for the Do method. The compiler only needs to infer S, which it can do from the return type of the lambda expression. It doesn't need to perform any inference for T, because that's already "known". (It's still generic, but it's not something which needs to be inferred for that method call.) The type of T will need to be supplied when constructing an instance of Manager in the first place, so it's effectively moving that decision.
For all the gory details of type inference, see section 7.5.2 of the C# 4 spec (or the equivalent section in the C# 3 or C# 5 specs). I'd advise a strong cup of coffee first though :)
I am working with a code that contains following overloaded method in generic class:
public class A<T>
{
public void Process(T item) { /*impl*/ }
public void Process(string item) { /*impl*/ }
}
When parametrizing the class for string do I lose the possibility to call the version with generic parameter?
var a = new A<string>();
a.Process(""); //Always calls the non-generic Process(string)
Specific types take precedence over generic types.
For example, this is what I tested with in LINQPad.
void Main()
{
new A<string>().Process("Hello");
}
public class A<T>
{
public void Process(T item) { Console.WriteLine("T"); }
public void Process(string item) { Console.WriteLine("string"); }
}
// Output: string
If you have a problem with hiding the generic method, then you need to rethink something. By overloading a generic method with specific types, you are effectively saying, "Use the generic overload if you need to, but if you can, use the specific version, because it should know what is best."
There is one way I just discovered, but it's a bit cross-eyed. Because generics and overloading get resolved in build time, you can define a generic method:
public static CallerClass
{
public static CallGenericOverload<T>(GenericClass<T> cls, T val)
{
return cls.ProblemOverload(val);
}
//We can also make an extension method.
//We don't have to of course, it's just more comfortable this way.
public static CallGenericOverloadExtension<T>(this GenericClass<T> cls, T val)
{
return cls.ProblemOverload(val);
}
}
public GenericClass<T>
{
public string ProblemOverload(T val)
{
return "ProblemOverload(T val)";
}
public string ProblemOverload(string val)
{
return "ProblemOverload(string val)";
}
}
Now, if we do the following:
var genClass = new GenericClass<string>();
Console.WriteLine(genClass.ProblemOverload("")); //output: ProblemOverload(string val)
Console.WriteLine(CallerClass.CallGenericOverload(genClass, "")); //output: ProblemOverload(T val)
Console.WriteLine(genClass.CallGenericOverloadExtension("")); //output: ProblemOverload(T val)
You can use a similar trick if you define a generic class instead of a generic method. The important thing is that the parameter you transfer to ProblemOverload needs to be of type T rather than type string in the invocation. After all, the method CallGenericOverload knows it's getting a T at build time, so it's going to bind to the overload that accepts the parameter. It doesn't matter that it's actually going to get a string at runtime.
Yes. This is documented in the C# spec, section 7.5.3, overload resolution.
From 7.5.3.6:
"While signatures as declared must be unique, it is possible that substitution of type arguments results in identical signatures. In
such cases, the tie-breaking rules of overload resolution above will
pick the most specific member."
The example given in there states that in the case below, overload resolution for G<int>.F1 will pick non-generic
class G1<U>
{
int F1(U u);
int F1(int i);
}
The tie-breaking rule that applies here is outlined in 7.5.3.2, "Better function member":
In case the parameter type sequences {P1, P2, …, PN} and {Q1, Q2, …,
QN} are equivalent (i.e. each Pi has an identity conversion to the
corresponding Qi), the following tie-breaking rules are applied, in
order, to determine the better function member.
If MP is a non-generic method and MQ is a generic method, then MP is better than MQ.
Having done this before, I'm inclined to say "No," but there's always more knowledgable folks who would argue otherwise.
If memory serves, the runtime compiler chooses the most strongly typed overload to execute.
CLARIFICATION
My answer is badly worded, and I deserve the downvote.
The OP asked, "When parametrizing the class for string do I lose the possibility to call the version with generic parameter?" I wasn't answering that "No, you can't do that," but that "No, you don't lose the ability to call the version with the generic parameter."
I should have been more clear.