C# generic overload - Compiler can't determine correct call - c#

I don't understand why the compiler can't resolve the correct overload to use here. (code below) There is only one version of Add() that is appropriate- BigFoo is an IFoo, and does not implement IEnumerable where T is an IFoo. But it insists on reporting an ambiguity. Any ideas? I tried adding a second generic type parameter- Add where T : IFoo where U : IEnumerable. But then the overload is completely ignored even for legitimate use.
I know I can work around this with casting and specifying generic type parameters but at that point I've defeated the purpose of having an overload. You could question the overload, but the semantics feel correct to me- the behavior I'm implementing in my class is for both Add() to add the object wholesale as an individual entry in the collection. (the second Add() is not supposed to be an AddRange().)
namespace NS
{
interface IFoo { }
class BigFoo : IFoo, IEnumerable<int>
{
public IEnumerator<int> GetEnumerator()
{
throw new NotImplementedException();
}
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
}
class FooContainer
{
public void Add(IFoo item) { }
public void Add<T>(IEnumerable<T> group) where T : IFoo { }
}
class DemoClass
{
void DemoMethod()
{
BigFoo bigFoo = new BigFoo();
FooContainer fooContainer = new FooContainer();
// error CS0121: The call is ambiguous between the following methods or properties:
// 'NS.FooContainer.Add(NS.IFoo)' and
// 'NS.FooContainer.Add<int>(System.Collections.Generic.IEnumerable<int>)'
fooContainer.Add(bigFoo);
}
}
}

Generic overload resolution doesn't take constraints into account, so it deems the Add<T> version to be applicable, inferring T=int.
Both methods are applicable, and neither is definitely better than the other, as there is no conversion between IEnumerable<int> and IFoo. While generic methods are deemed "less specific" than non-generic methods, this only becomes relevant when the parameter types are identical after type argument replacement, which they're not in this case.

In FooContainer, on the second "Add" you are constraining T to be of type IFoo. BigFoo implements the IFoo interface, therefore it kinda matches that Add definition (even though it doesn't really, because it doesn't implement IEnumable<IFoo>).
I'm not sure I understand completely what you want, but I suspect it is this:
public void Add<T>(T group) where T : IEnumerable<IFoo> { }
which would allow you to add any object T where T is an enumerable set of IFoo objects.
Is that what you wanted?
Regards,
Richard

The problem here is that generic type constraints are completely ignored by the compiler (it only looks at parameter types). As far as the compiler is concerned, the IEnumerable<T> argument being passed could just as well be a IEnumerable<IFoo>.
For complete information on this subject, refer to section 25.6.4 Inference of type arguments of the C# Language Specification. Note that there is no mention of the utilisation of type constraints.

The compiler should be smart enough to recognize that BigFoo can't be cast to IEnumerable<IFoo>, but it isn't. It simply sees that it's an IEnumerable<T>, and feels that it's a potential overload candidate (even though the contstraint you defined enforces that T must be IFoo and int can't be cast to IFoo). While it's inconvenient, it's not that big of a deal. Just cast bigFoo to IFoo and the compiler will be happy:
fooContainer.Add((IFoo)bigFoo);
Alternately, you can make your generic overload of Add uglier:
public void Add<T, U>(U group)
where T : IFoo
where U : IEnumerable<T>
{
}
Either way, you have more typing, the second solution eliminates the need to cast calls to Add, but you will have to explicitly declare type on calls to the generic add (which ends up being more code:
fooContainer.Add<IFoo, IEnumerable<IFoo>>(enumerableFoo);

Interesting.... Just tried your sample out. Generics continues to keep me on my toes.
//1 - First preference
public void Add(BigFoo item) { Console.WriteLine("static BigFoo type Add"); }
//2 - Second Preference
public void Add<T>(T item) { Console.WriteLine("Generic Add"); }
//3 - Third preferences
public void Add(IFoo item) { Console.WriteLine("static IFoo interface Add"); }
//4 - Compiles if 1-4 exist. Compile error (ambiguity) if only 3-4 exist. Compile error (cannot convert int to IFoo) if only 4 exists
public void Add<T>(IEnumerable<T> group) where T : IFoo { }

Related

Is it possible to use the generic "in" and "out" modifier on the same T?

This code is invalid because T can't have the in and out modifier at the same time:
public interface IInOut<in out T>
{
}
But you can do this "workaround":
public interface IInOutWorkaround<in TIn, out TOut>
{
TOut Test(TIn value);
}
public class InOutWorkaround<T> : IInOutWorkaround<T, T>
{
public T Test(T value)
{
throw new NotImplementedException();
}
}
The second example works and the InOutWorkaround class has the same type for TIn and TOut, so why is it not possible to add both modifier to the same T directly in the interface? Or is it possible with a different syntax?
in T says that T can not be used covariantly, and out T says that T can not be used contravariantly. Your in out T would therefore mean that the type can not be used covariantly and can not be used contravariantly, which means it'd be invariant. So in effect that would behave identically to just writing public interface IInOut<T>, because when no in or out modifiers are used the generic type is considered invariant.
In the case of your class InOutWorkaround<T>, T is still invariant, so the fact that you're using it as both an in and out type is fine, because it's invariant, as it meets both restrictions. If you were attempting to have a type that could be used both covariantly and contravariantly, your workaround didn't achieve that, because the T in InOutWorkaround is invariant (because all generic type arguments for all classes are invariant). That generic type argument cannot be used either covariantly or contravariantly.
One could have interfaces IReadable<out T> { T read(int index); }, IWritable<in T> { void write(int index, T dat);, ISplitReadWrite<out Tout, in Tin>:IReadable<Tout>,IWritable<Tin>, and IReadWrite<T>:ISplitReadWrite<T,T>.
If one has a class MyCollection<T> which implements IReadWrite<T>, then a MyCollection<Cat> could be converted to IReadable<Animal>, IWritable<SiameseCat>, or an ISplitReadWrite<Animal,SiameseCat>. Note, however, that the only IReadable<T> that would yield an item that could be stored into a MyCollection<Cat> would be IReadable<Cat>, the only IWritable<T> that could handle everything that might appear in a MyCollection<Cat> would be IWritable<Cat>. The only forms of ISplitReadWrite<Tout,Tin> that would allow one to read out an item and write it back to the same collection without a cast would be those where the two types were the same, and the only such type implemented by MyCollection<Cat> would be ISplitReadWrite<Cat,Cat>.
Note that one could have an interface with methods that could be equally usable with MyCollection<Animal> and MyCollection<SiameseCat>, such as "swap the items in slots i1 and i2 of the same collection", but such an interface wouldn't need any generic parameter at all. Id one has an IPermutable interface, it could include methods like void swapItems(int i1, int i2); which wouldn't have any generic types in their signatures, and thus wouldn't make it necessary for the type to include any generic type arguments.
According to Extending Variant Generic Interfaces specification
The compiler does not infer the variance from the interface that is
being extended. You can create an interface that extends both the interface where the
generic type parameter T is covariant and the interface where it is
contravariant if in the extending interface the generic type parameter
T is invariant.
interface ICovariant<out T> { }
interface IContravariant<in T> { }
interface IInvariant<T> : ICovariant<T>, IContravariant<T> { }
This example looks like as an exactly your case, T is invariant generic type parameter in InOutWorkaround<T> interface, compiler doesn't infer (or inherit it in extending interface), so your workaround is pointless
public class InOutWorkaround<T> : IInOutWorkaround<T, T>
{
public T Test(T value)
{
throw new NotImplementedException();
}
}

Using generic contravariant with IList and IEnumerable

I'm learning C# generics and making some dummy code for testing purposes. So, I'm testing the in Generic Modifier, which specifies that the type parameter is contravariant.
Given the below interface:
public interface IInterfaceTest<in T>
{
void Method(T value);
void Method(IList<T> values);
void Method(IEnumerable<T> values);
}
When compiling, I'm getting the error message:
[CS1961] Invalid variance: The type parameter 'T' must be invariantly
valid on 'IInterfaceTest.Method(IList)'. 'T' is contravariant.
The error is related only with the line void Method(IEnumerable<T> values). If this line is removed, all works fine.
So my question is: Why can I use the generic contravariant with IEnumerable but does not with IList? Am I forgot something?
Thanks.
The question why it's not allowed for IList<T> has been answered in the comments and linked questions already: IList<T> is invariant in T and so a contra-variant T cannot be used here whatsoever.
What puzzled me at first is the fact that Method(IEnumerable<T>) is allowed here. The strange thing is that variance is "turned around" when you use the T as a type argument for another generic type.
Imagine this.
public interface ITestInterface<in T>
{
void Method(IEnumerable<T> e);
IEnumerable<T> GetMethod(); // illegal
}
public class Animal {}
public class Lion : Animal [}
public class Gnu : Animal {}
ITestInterface<Animal> animals;
ITestInterface<Lion> lions;
ITestInterface<Gnu> gnus;
Now the contra-variance of ITestInterface<in T> in T tells us that you can do
lions = animals;
And when you call lions.Method(e), you can only provide an IEnumerable<Lion>. So the code of Method can only enumerate Lions, which are all Animals as animals.Method() expects. Everything is fine.
On the other hand, the IEnumerable<T> GetMethod() is illegal, because:
gnus = animals;
is legal, and now gnu.GetMethod() would return an IEnumerable<Animal> where you'd expect an IEnumerable<Gnu>. And when you iterated, suprising animals could wait in that sequence.

Overloading methods of an Interface based on return type...

I cant seem to find anything that explicitly states this should never be done, not can i find a recommended method for doing it. so I am beginning to thing I am on an entirely wrong track here...
I am trying to overload a function based on return type in an interface. Basically I have 15 functions in an interface, 9 of them have the same name/params but different return type, I am trying to isolate this so I dont have to write 15 functions in the interface, I would just like a few..
public interface IController
{
IEnumerable<T> Fetch<T>();
}
from here I want to do implementations such as...
public IEnumerable<T> Fetch<T>() where T : *class*
{
return dbContext.*clas*.ToList();
}
however I recieve a compilation error of...
Error 1 The constraints for type parameter 'T' of method 'Controllers.Controller.Fetch<T>()' must match the constraints for type parameter 'T' of interface method 'IController.Fetch<T>()'. Consider using an explicit interface implementation instead.
anyone have any ideas on this one...
You can't do this because this implementation conflicts with the Liskov Substitution principle.
A method can only become more wide (accept more) than the the classes/interfaces above in the type hierarchy.
Now C# does not fully support the Liskov Substition principle (in the sense that widening parameters is not allowed). But it means for instance that if a method
public class Foo {
void Bar (T parameter);
}
is defined in the first level, that method cannot be override with
public class SubFoo : Foo {
void Bar (SubT parameter);
}
This is because one can call the Bar method of a SubFoo on the Foo level. And the Foo level has a contract that it accepts T. So making the types more narrow is not an option.
If one thus moves down in the class hierarchy one notices that:
return types become more narrow; and
parameters become wider
C# however supports variance/covariance on the interface level. If T is thus only used to specify the output type, one can indeed make T more narrow. This is called variance. You can specify this as:
public interface Foo<out T> {
T GetValue ();
}
It means that Foo<T> is a subclass of Foo<SuperT>. The same for covariance:
public interface Foo<in T> {
void SetValue (T value);
}
Making a few assumptions...if you're talking about an EF dbContext, you can actually do this:
public IEnumerable<T> Fetch<T>() where T : class
{
return dbContext.Set<T>().ToList();
}
More generally, you can do something like this, where the generic method delegates to various implementation methods for the different types:
public IEnumerable<T> Fetch<T>() where T : class
{
if (typeof(T) == typeof(X)) return FetchX();
//Handle other types here...
}
As Servy pointed out, to implement the above you would also need to modify your interface to include the class constraint (assuming you need it):
public interface IController
{
IEnumerable<T> Fetch<T>() where T: class;
}

How can I create a covariant extension method on a generic interface in C#?

If the title didn't make sense, here's an example:
interface IEat { void Eat; }
class Cat : IEat { void Eat { nom(); } }
class Dog : IEat { void Eat { nom(); nom();nom(); } }
class Labrador : Dog { }
I'd like to create an extension method like this:
public static void FeedAll(this IEnumerable<out IEat> hungryAnimals) {
foreach(var animal in hungryAnimals) animal.Eat();
}
So I can do this:
listOfCats.FeedAll();
listOfLabs.FeedAll();
listOfMixedHungryAnimals.FeedAll();
Is this possible? Where did I go wrong?
The real-world application here is that "Dog" is a major base class in my application, and there are many subclasses, each of which may have ILists of things from time to time that need to have group operations performed on them. Having to cast them just to call an extension method on a List of an interface they all implement would be suboptimal.
Edit:
I goofed up the example a little. My actual code is using IList, I was hoping to have Count and index-based operations available. Based on the answers below, I guess I'll have to go another direction for the methods that require IList semantics.
IEnumerable is already covariant, so your extension method can accept an IEnumerable<IEat> and an IEnumerable<Dog> instance will be a valid argument, making the extension method apply to variables of those types.
Had the definition of the interface not specified that the generic argument was covariant/contravariant then there would be nothing that you extension method could do to allow that argument to be covariant. If you were using, say, a List which is invariant, there is nothing your extension method can do to allow for the use of covariance.
This will work if you remove the out:
public static void FeedAll(this IEnumerable<IEat> hungryAnimals) {
foreach(var animal in hungryAnimals) animal.Eat();
}
variance applies to parameters of the the interface itself (T in IEnumerable<T> in this case) so a List<Dog> is compatible with IEnumerable<IEat>.
As Servy notes, Enumerable<T> is already covariant, but it doesn't need to be. For older versions where the interface isn't covariant, you could do this:
public static void FeedAll<T>(this IEnumerable<T> hungryAnimals) where T : IEat
{
foreach(var animal in hungryAnimals) animal.Eat();
}
You don't need the out it gets passed by reference so it will work with out it.

Unexpected Generics Behavior

I was discussing a related matter with someone on chat and I came up with this code which behaved differently than I had expected.
class Program
{
static void Main(string[] args)
{
new Test<SomeObject>();
Console.ReadKey();
}
}
class SomeObject
{
public SomeObject() { }
public new string ToString()
{
return "Hello world.";
}
}
class Test<T> where T : new()
{
public Test()
{
T t = new T();
object t1 = t;
Console.WriteLine(t.ToString());
Console.WriteLine(t1.ToString());
}
}
The output is:
<ProjectName>.SomeObject
<ProjectName>.SomeObject
Because the first line is written from the generic type I expected it to use the ToString() method defined in SomeObject since that's what the type would become at run time wouldn't it?
I believe that Ben Voigt has given you the answer in his comment.
You could achieve the result you’re expecting by specifying the type that declares the hiding (new) method implementation as a generic constraint:
class Test<T> where T : SomeObject, new()
{
public Test()
{
T t = new T();
object t1 = t;
Console.WriteLine(t.ToString());
Console.WriteLine(t1.ToString());
}
}
This outputs:
Hello world.
Program.SomeObject
Edit: The compiler resolves any member invocations on generic types against the generic constraints. This is implied in the MSDN C# Programming Guide on Constraints on Type Parameters:
By constraining the type parameter, you increase the number of allowable operations and method calls to those supported by the constraining type and all types in its inheritance hierarchy. Therefore, when you design generic classes or methods, if you will be performing any operation on the generic members beyond simple assignment or calling any methods not supported by System.Object, you will have to apply constraints to the type parameter.
To help clarify matters: Imagine that you had defined a new method, Foo, in your class:
class SomeObject
{
public SomeObject() { }
public void Foo() { }
}
Attempting to call Foo would result in a compile-time error. The only thing the compiler knows about generic type T is that it has a parameterless constructor – it has no knowledge of any methods it might define.
class Test<T> where T : new()
{
public Test()
{
T t = new T();
t.Foo(); // Error: 'T' does not contain a definition for 'Foo'
// and no extension method 'Foo' accepting a
// first argument of type 'T' could be found
}
}
However, if you constrain T to be of type SomeObject, then the compiler would know to look for the definition of Foo within the SomeObject class:
class Test<T> where T : SomeObject, new()
{
public Test()
{
T t = new T();
t.Foo(); // SomeObject.Foo gets called
}
}
The reasoning is quite similar for hidden members.
In Test<T>, the compiler doesn't know that T will actually be SomeObject, since there's no constraint on T. So it can only assumes that t is an object, and the call to t.ToString() results in calling the virtual Object.ToString method, not SomeObject.ToString()
This has absolutely nothing to do with generics, and everything to do with the fact that you declared a new method called ToString, instead of overriding the existing one.
If you had overridden the method instead, it would've been used, this has nothing to do with early-bound compared to late-bound either (as indicated by other answers here.)
The reason why calling ToString on the T reference does not differ from the object reference is that the compiler has no way to verify that all T's that can be used here has defined that new ToString method, and it thus has to fall back to the one inherited from object in all cases.
Note that the compiler will produce a method that will be used by all variations of T, regardless, so it has to use the knowledge it has about T, and you have not stated that you inherit from an interface or a class (although in this case I doubt it would make a difference) so the compiler has no way of knowing that ToString has been overridden in this case.
If, on the other hand, you had stated that the T in question is a descendant of SomeObject, the compiler would know that it has a new ToString method to use, but this knowledge is not available to the compiler with just the T alone.

Categories

Resources