I am trying understand OOP concepts in C#.
In the following sample code:
why the ins1 prefers generic method
why the ins2, ins3 prefers non-generic method
Note:when I comment out either of the "MyTestMethod" method, still the program continues to run successfully. This snippet is not something from production. This is just my training sample. So, please do not mind the naming conventions and standards.
using System;
namespace ConsoleApplication1
{
class Program
{
public static void MyTestMethod(J input)
{
Console.WriteLine($"Program.MyTestMethod: {input.Val}");
}
public static void MyTestMethod<T>(T input) where T : J
{
Console.WriteLine($"Program.MyTestMethod<T>: {input.Val}");
}
static void Main(string[] args)
{
J2 ins1 = new J2(1);
MyTestMethod(ins1);
J ins2 = new J(2);
MyTestMethod(ins2);
J ins3 = new J2(3);
MyTestMethod(ins3);
Console.ReadKey();
}
}
internal class J
{
public int Val { get; set; }
public J(int i)
{
Console.WriteLine($"concrete base {i}");
Val = i;
}
}
internal class J2 : J
{
public J2(int i) : base(i * -1)
{
Console.WriteLine($"concrete {i}");
}
}
}
Section 7.5.3.2 of the C# specification is the relevant part here - "Better function member".
The result is more simply demonstrated as:
using System;
class Test
{
static void Foo<T>(T item)
{
Console.WriteLine("Generic");
}
static void Foo(object x)
{
Console.WriteLine("Non-generic");
}
static void Main()
{
Foo(new object()); // Calls Foo(object)
Foo("test"); // Calls Foo<T>(T)
}
}
In both calls, both overloads are applicable function members. In picking which overload to call, the compiler first checks which conversion from argument type (or expression) to parameter type is "better".
When the type of the argument is object, T is inferred to be object as well, and so for both candidates the conversion is the identity conversion of object to object. At that point, the tie-breaking rules of 7.5.3.2 get involved, and the first one is:
If MP is a non-generic method and MQ is a generic method, then MP is better than MQ.
So that's why the non-generic overload is picked in that case.
When the argument is of type string, T is inferred to be string, so we have to compare the conversion from string to string (for the generic method) to the conversion from string to object (for the non-generic method). Here section 7.5.3.3 of the C# spec comes in, which starts:
Given an implicit conversion C1 that converts from an expression E to a type T1, and an implicit conversion C2 that converts from an expression E to a type T2, C1 is a better conversion than C2 if at least one of the following holds:
E has a type S and an identity conversion exists from S to T1 but not from S to T2
In this context, E is the expression "test", S is the type string, T1 is the type string, T2 is the type object, and so the conversion from string to string is deemed better - and the generic method is picked.
why the ins2,3 prefers non-generic method
Because in both cases, the type of the variable (not the instantiated type) is J, and that is an exact match for your method with signature MyTestMethod(J input). Hence, the overload resolution rules tells it takes that one.
why the ins1 prefers generic method
Since T can be J2 in the method MyTestMethod(T input) where T : J, and it can strongly type T to be J2, again, it is a better match than the base class J, from your other method. So it matches the method with type arguments.
Related
This code is not compiled:
static class Foo
{
public static Bar Do(Func<Bar> f) => null;
public static Bar<TOut> Do<TOut>(Func<Bar<TOut>> f) => null;
}
public class Bar
{
}
public class Bar<TOut>
{
public static implicit operator Bar<TOut>(TOut i) => null;
}
// Here compiler complains:
// CS0029 Cannot implicitly convert type 'int' to 'Bar'
// CS1662 Cannot convert lambda expression to intended delegate type
// because some of the return types in the block
// are not implicitly convertible to the delegate return type
Foo.Do(() => 1);
My expectation would be that compiler sees the return type of the lambda and that no valid overload can be selected unless int is converted to Bar<int>.
However, I see that compiler resolves to the first method.
Which part of spec defines this behavior?
This is specified in Method Invocations, when the spec is talking about what method declarations count as a candidate for overload resolution, for an invocation of the form M(A):
The set of candidate methods for the method invocation is constructed. For each method F associated with the method group M:
If F is non-generic, F is a candidate when:
M has no type argument list, and
F is applicable with respect to A.
If F is generic and M has no type argument list, F is a candidate when:
Type inference succeeds, inferring a list of type arguments for the call, and
[...]
Just from those rules, we can see that the non-generic Do is a candidate, and the generic Do is not, because type inference fails. Try commenting out the non-generic Do, and you will see that it says something like "type arguments cannot be inferred".
I do not have the full answer, but I have some observations:
Observation 1: With removing the non-generic version of Do:
static class Foo
{
public static Bar Do(Func<Bar> f) => null;
}
public class Bar
{
public static implicit operator Bar(int i) => null;
}
// CS0411: The type arguments for method 'Foo.Do<TOut>(Func<Bar<TOut>>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
Foo.Do(() => 1);
, still the compiler can't resolve Func<Bar<TOut>> f. So it does not seem that the issue is about picking the wrong overload, but rather compiler not being able to match () => 1 to Func<Bar<TOut>> f implicitly at all.
Observation 2: The code below works
static class Foo
{
public static Bar Do(Func<Bar> f) => null;
}
public class Bar
{
public static implicit operator Bar(int i) => null;
}
Foo.Do(() => 1);
which shows the compiler is checking implicit conversions.
Observation 3: Making the implicit cast operator to take int as in input, eventhough makes the operator not practically usable as TOut will not be resolved, makes the compiler find the operator:
static class Foo
{
public static Bar<TOut> Do<TOut>(Func<Bar<TOut>> f) => null;
}
public class Bar<TOut>
{
public static implicit operator Bar<TOut>(int i) => null; // Input is `int` now
}
// CS0411: The type arguments for method 'Foo.Do<TOut>(Func<Bar<TOut>>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
Foo.Do(() => 1);
So all of this combined makes me think that the compiler is just not trying to which generic types would cause the implicit conversion to work.
Recently I've experimented with an implementation of the visitor pattern, where I've tried to enforce Accept & Visit methods with generic interfaces:
public interface IVisitable<out TVisitable> where TVisitable : IVisitable<TVisitable>
{
TResult Accept<TResult>(IVisitor<TResult, TVisitable> visitor);
}
-whose purpose is to 1) mark certain type "Foo" as visitable by such a visitor, which in turn is a "visitor of such type Foo" and 2) enforce Accept method of the correct signature on the implementing visitable type, like so:
public class Foo : IVisitable<Foo>
{
public TResult Accept<TResult>(IVisitor<TResult, Foo> visitor) => visitor.Visit(this);
}
So far so good, the visitor interface:
public interface IVisitor<out TResult, in TVisitable> where TVisitable : IVisitable<TVisitable>
{
TResult Visit(TVisitable visitable);
}
-should 1) mark the visitor as "able to visit" the TVisitable 2) what the result type (TResult) for this TVisitable should be 3) enforce Visit method of a correct signature per each TVisitable the visitor implementation is "able to visit", like so:
public class CountVisitor : IVisitor<int, Foo>
{
public int Visit(Foo visitable) => 42;
}
public class NameVisitor : IVisitor<string, Foo>
{
public string Visit(Foo visitable) => "Chewie";
}
Quite pleasantly & beautifully, this lets me write:
var theFoo = new Foo();
int count = theFoo.Accept(new CountVisitor());
string name = theFoo.Accept(new NameVisitor());
Very good.
Now the sad times begin, when I add another visitable type, like:
public class Bar : IVisitable<Bar>
{
public TResult Accept<TResult>(IVisitor<TResult, Bar> visitor) => visitor.Visit(this);
}
which is visitable by let's say just the CountVisitor:
public class CountVisitor : IVisitor<int, Foo>, IVisitor<int, Bar>
{
public int Visit(Foo visitable) => 42;
public int Visit(Bar visitable) => 7;
}
which suddenly breaks the type inference in the Accept method! (this destroys the whole design)
var theFoo = new Foo();
int count = theFoo.Accept(new CountVisitor());
giving me:
"The type arguments for method 'Foo.Accept<TResult>(IVisitor<TResult, Foo>)' cannot be inferred from the usage."
Could anyone please elaborate on why is that? There is only one version of IVisitor<T, Foo> interface which the CountVisitor implements - or, if the IVisitor<T, Bar> can't be eliminated for some reason, both of them have the same T - int, = no other type would work there anyway. Does the type inference give up as soon as there are more than just one suitable candidate? (Fun fact: ReSharper thinks the int in theFoo.Accept<int>(...) is redundant :P, even though it wouldn't compile without it)
It seems that the type inference works in a greedy way, first trying to match the method generic types, then the class generic types. So if you say
int count = theFoo.Accept<int>(new CountVisitor());
it works, which is strange, since Foo is the only candidate for the class generic type.
First, if you replace the method generic type with a second class generic type, it works:
public interface IVisitable<R, out T> where T: IVisitable<int, T>
{
R Accept(IVisitor<R, T> visitor);
}
public class Foo : IVisitable<int, Foo>
{
public int Accept(IVisitor<int, Foo> visitor) => visitor.Visit(this);
}
public class Bar : IVisitable<int, Bar>
{
public int Accept(IVisitor<int, Bar> visitor) => visitor.Visit(this);
}
public interface IVisitor<out TResult, in T> where T: IVisitable<int, T>
{
TResult Visit(T visitable);
}
public class CountVisitor : IVisitor<int, Foo>, IVisitor<int, Bar>
{
public int Visit(Foo visitable) => 42;
public int Visit(Bar visitable) => 7;
}
class Program {
static void Main(string[] args) {
var theFoo = new Foo();
int count = theFoo.Accept(new CountVisitor());
}
}
Second (and this is the strange part which highlights how the type inference works) look what happens if you replace int with string in the Bar visitor:
public class CountVisitor : IVisitor<int, Foo> , IVisitor<string, Bar>
{
public int Visit(Foo visitable) => 42;
public string Visit(Bar visitable) => "42";
}
First, you get the same error, but watch what happens if you force a string:
int count = theFoo.Accept<string>(new CountVisitor());
error CS1503: Argument 1: cannot convert from 'CountVisitor' to
'IVisitor<string, Foo>'
Which suggests that the compiler first looks at the method generic types (TResult in your case) and fails immediately if it finds more candidates. It doesn't even look further, at the class generic types.
I tried to find a type inference specification from Microsoft, but couldn't find any.
Does the type inference give up as soon as there are more than just one suitable candidate?
Yes, in this case it does. While attempting to infer the method's generic type parameter (TResult), the type inference algorithm appears to fail on CountVisitor having two inferences to the type IVisitor<TResult, TVisitable>.
From the C# 5 specification (the most recent I could find), §7.5.2:
Tr M<X1…Xn>(T1 x1 … Tm xm)
With a method call of the form M(E1 …Em) the task of type inference is to find unique type arguments
S1…Sn for each of the type parameters X1…Xn so that the call M<S1…Sn>(E1…Em) becomes valid.
The very first step the compiler takes is as follows (§7.5.2.1):
For each of the method arguments Ei:
If Ei is an anonymous function, an explicit parameter type inference (§7.5.2.7) is made from Ei
to Ti
Otherwise, if Ei has a type U and xi is a value parameter then a lower-bound inference is made from U to Ti.
You only have one argument, so we have that the only Ei is the expression new CountVisitor(). It's clearly not an anonymous function, so we're in the second bullet point. It's trivial to see that in our case, U is of type CountVisitor. The "xi is a value parameter" bit basically means it's not an out, in, ref etc. variable, which is the case here.
At this point, we now need to make a lower-bound inference from CountVisitor to IVisitor<TResult, TVisitable> The relevant part of §7.5.2.9 (where due to a variable switch, we have V = IVisitor<TResult, TVisitable> in our case):
Otherwise, sets U1…Uk and V1…Vk are determined by checking if any of the following cases apply:
V is an array type V1[…] and U is an array type U1[…] (or a type parameter whose effective base type is U1[…]) of the same rank
V is one of IEnumerable<V1>, ICollection<V1> or IList<V1> and U is a one-dimensional array type U1[] (or a type parameter whose effective base type is U1[])
V is a constructed class, struct, interface or delegate type C<V1…Vk> and there is a unique type C<U1…Uk> such that U (or, if U is a type parameter, its effective base class or any member of its effective interface set) is identical to, inherits from (directly or indirectly), or implements (directly or indirectly) C<U1…Uk>.
(The “uniqueness” restriction means that in the case interface C<T>{} class U: C<X>, C<Y>{}, then no inference is made when inferring from U to C<T> because U1 could be X or Y.)
We can skip past the first two cases as they're clearly not applicable, the third case is the one we fall into. The compiler attempts to find a unique type C<U1…Uk> that CountVisitor implements and finds two such types, IVisitor<int, Foo> and IVisitor<int, Bar>. Note that the example the spec gives is nearly identical your example.
Because of the uniqueness constraint, no inference is made for this method argument. With the compiler not able to infer any type information from the argument, it has nothing to go on to try to infer TResult and thus fails.
As to why there exists a uniqueness constraint, my guess is that it simplifies the algorithm and thus compiler implementation. If you're interested, here's a link to source code where Roslyn (modern C# compiler) implements generic method type inference.
In C#, you can simplify the Visitor pattern by removing the 'double dispatch' by making use of the dynamic keyword.
You can implement your Visitor like this:
public class CountVisitor : IVisitor<int, IVisitable>
{
public int Visit( IVisitable v )
{
dynamic d = v;
Visit(d);
}
private int Visit( Foo f )
{
return 42;
}
private int Visit( Bar b )
{
return 7;
}
}
By doing this, you won't need to have the Accept method implemented on Foo and Bar although they still must implement a common interface for the Visitor to work offcourse.
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.
public interface ILovable<T> where T : IEquatable<T>
{
T Care(T t);
}
public class Me : ILovable<int>
{
public int Care(int i)
{
return i;
}
}
Say I have the above. Now below function fails:
private static void Colour<T>(ILovable<T> me) where T : IEquatable<T>
{
var z = me.Care(1); //cannot convert from 'int' to 'T'
}
What's failing the above piece of code? ILovable<T> has a Care function which intakes a T which is IEquatable<T>. In the above function I'm calling the same Care function and passing T which is int type. int is after all IEquatable<int>.
What am I doing wrong? Is there any work around to get it fixed?
Your method signature does not specify a ILovable<int>, it specifies an ILovable<T>. This, for example, would work:
private static void Colour(ILovable<int> me)
{
var z = me.Care(1); //cannot convert from 'int' to 'T'
}
The problem is the compiler doesn't know that T is an 'int' in your example; it could be any type that meets the constraint. Here is another way that would work:
private static void Colour<T>(ILovable<T> me, T valueToCareAbout) where T : IEquatable<T>
{
var z = me.Care(valueToCareAbout);
}
//use like this
Colour(me, 1);
The error I get is:
Argument type 'int' is not assignable to parameter type 'T'
I'm pretty sure this is because you are defining me as an ILovable<T>. Therefore, it doesn't automatically resolve to the Me type where int is defined as T.
This will fix the error because Me defines T as an int:
private static void Colour<T>(Me me) where T : IEquatable<T>
{
var z = me.Care(1);
}
Well that's because method Colour says that there will be parameter of type ILovable< T > whereas T would be resolved later, so at compile time either I tell method that T is int type.
So either you pass ILovable as parameter and grantee that T is int
void Colour<T>(ILovable<int> me)
or pass type Me directly
void Colour<T>(Me me)
Because otherwise me.Care is expecting type T not int as specific
Change following
private static void Colour<T>(ILovable<T> me) where T : IEquatable<T>
To
private static void Colour<Int32>(ILovable<int> me)
and above will work.
Now the mystry portion
You are getting error in following
private static void Colour<T>(ILovable<T> me) where T : IEquatable<T>
because Care is expecting T, and you are providing int.
It is same as
Care((T)1).
or
T t = (T)1; //This is the cause of error as int cannot be changed to T. Remember Int32 is sealed so T cannot derive from int
Care(t); // This is fine
To make above work, T has to int. To make it so, Colur method syntax should be like
private static void Colour<Int32>(ILovable<int> me)
If you want to pass string to Care, T should be string.
private static void Colour<string>(ILovable<string> me)
{
me.Care("Hello");
}
Now if we have to fix T then question arises why T is required at all in Colour definition.
Answer -> For non taking care of inheritance type in non sealed class.
The short answer is overriding variable of type T inside a generic method (or class) with a more derived type is not possible since compiler doesn't explicitly know T is that more derived type (in our case T is int), because T can be any other more derived type at run time.
Long answer: me variable is of type ILovable<T>. Now me.Care function is expecting parameter of type that is specified on ILovable<T> which is T. Outside the Care function T can be anything that is IEquatable<T>, so int is ok. But inside the function, T has to be just T and not another derived type of IEquatable<T>. Otherwise there will be runtime error for scenarios like this:
private static void Colour<T>(ILovable<T> me) where T : IEquatable<T>
{
var z = me.Care(1);
}
...
Colour("");
Right now, T is string when calling Colour(""). So me is ILovable<string>. So me.Care function expects a string as parameter but provided is an int and that is disaster.
Eric Lippert has explained in his blog post at http://blogs.msdn.com/b/ericlippert/archive/2009/12/10/constraints-are-not-part-of-the-signature.aspx why constraints are not considered for type inference, which makes sense given that methods cannot be overloaded by simply changing type constraints. However, I would like to find a way to instantiate an object using two generic types, one which can be inferred and another which could be inferred if constraints were considered, without having to specify any of the types.
Given the types:
public interface I<T>
{
Other<T> CreateOther();
}
public class C : I<string>
{
public Other<string> CreateOther()
{
return new Other<string>();
}
}
public class Other<T>
{
}
and the factory:
public static class Factory1
{
public static Tuple<T, Other<T1>> Create<T, T1>(T o) where T : I<T1>
{
return new Tuple<T, Other<T1>>(o, o.CreateOther());
}
}
the following desired code will not compile:
public void WontCompile()
{
C c = new C();
var v = Factory1.Create(c); // won't compile
}
The error message is "error CS0411: The type arguments for method 'yo.Factory1.Create(T)' cannot be inferred from the usage. Try specifying the type arguments explicitly.", which is in line with what Eric said in his blog post.
Thus, we can simply specify the generic type arguments explicitly, as the error message suggests:
public void SpecifyAllTypes()
{
C c = new C();
var v = Factory1.Create<C, string>(c); // type is Tuple<C, Other<string>>
}
If we don't wish to specify type arguments and we don't need to retain type C, we can use the following factory:
public static class Factory2
{
public static Tuple<I<T1>, Other<T1>> CreateUntyped<T1>(I<T1> o)
{
return new Tuple<I<T1>, Other<T1>>(o, o.CreateOther());
}
}
and now specify:
public void Untyped()
{
C c = new C();
var v = Factory2.CreateUntyped(c); // type is Tuple<I<string>, Other<string>>
}
However, I wish to retain type C in the returned object and not specify the types.
I came up with a solution to this problem, but it seems to be a kludge of a workaround, where the object of type C is used twice in a two-step factory call.
To do this, the following factories are used:
public static class Factory3
{
public static Factory<T1> CreateFactory<T1>(I<T1> o)
{
return new Factory<T1>();
}
}
public class Factory<T1>
{
public Tuple<T, Other<T1>> Create<T>(T o) where T : I<T1>
{
return new Tuple<T, Other<T1>>(o, o.CreateOther());
}
}
which can then be used as follows:
public void Inferred()
{
C c = new C();
var v = Factory3.CreateFactory(c).Create(c); // type is Tuple<C, Other<string>>
}
This just feels odd since c is used twice. The first time it is used it is actually discarded as it is just being used to infer the base type argument.
Is there a better solution to this problem where the object does not need to be used twice and the types do not need to be specified?
edit: I just realized that, although the object must be used twice, the second factory class is not needed. Rather, both parameters could just be used in the same factory method as follows:
public class Factory
{
public Tuple<T, Other<T1>> Create<T, T1>(T o, I<T1> o2) where T : I<T1>
{
return new Tuple<T, Other<T1>>(o, o.CreateOther());
}
}
This would be used as follows:
public void Inferred()
{
C c = new C();
var v = Factory.Create(c, c); // type is Tuple<C, Other<string>>
}
It's still not ideal, but better than having to create a second factory class, and at least XMLDoc comments could be used to indicate that both parameters should be the same object. Once again, the one parameter (o2 in this case) is only used to infer the constrained types for T.