I ran across a compilation issue today that baffled me. Consider these two container classes.
public class BaseContainer<T> : IEnumerable<T>
{
public void DoStuff(T item) { throw new NotImplementedException(); }
public IEnumerator<T> GetEnumerator() { }
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { }
}
public class Container<T> : BaseContainer<T>
{
public void DoStuff(IEnumerable<T> collection) { }
public void DoStuff <Tother>(IEnumerable<Tother> collection)
where Tother: T
{
}
}
The former defines DoStuff(T item) and the latter overloads it with DoStuff <Tother>(IEnumerable<Tother>) specifically to get around the absence of covariance/contravariance of C# (until 4 I hear).
This code
Container<string> c = new Container<string>();
c.DoStuff("Hello World");
hits a rather strange compilation error. Note the absence of <char> from the method call.
The type ‘char’ cannot be used as type parameter ‘Tother’ in the generic type or method ‘Container.DoStuff(System.Collections.Generic.IEnumerable)’. There is no boxing conversion from ‘char’ to ‘string’.
Essentially, the compiler is trying to jam my call to DoStuff(string) into Container.DoStuff<char>(IEnumerable<char>) because string implements IEnumerable<char>, rather than use BaseContainer.DoStuff(string).
The only way I’ve found to make this compile is to add DoStuff(T) to the derived class
public class Container<T> : BaseContainer<T>
{
public new void DoStuff(T item) { base.DoStuff(item); }
public void DoStuff(IEnumerable<T> collection) { }
public void DoStuff <Tother>(IEnumerable<Tother> collection)
where Tother: T
{
}
}
Why is the compiler trying to jam a string as IEnumerable<char> when 1) it knows it can’t (given the presence of a compilation error) and 2) it has a method in the base class that compiles fine? Am I misunderstanding something about generics or virtual method stuff in C#? Is there another fix other than adding a new DoStuff(T item) to Container?
Edit
Ok… I think I see your confusion now. You would have expected DoStuff(string) to have kept the parameter as a string and walked the BaseClass Method List first looking for a suitable signature, and failing that fallback to trying to cast the parameter to some other type.
But it happened the other way around… Instead
Container.DoStuff(string)went, meh “theres a base class method there that fits the bill, but I’m going to convert to an IEnumerable and have a heart attack about what’s available in the current class instead…Hmmm… I’m sure Jon or Marc would be able to chime in at this point with the specific C# Spec paragraph covering this particular corner case
Original
Both Methods expect an IEnumerable Collection
You’re passing an individual string.
The compiler is taking that string and going,
COMPILER ERROR
So what#s the fix, well it completely depends what your trying to achieve… both of the following would be valid, essentially, your types usage is just incorrect in your incarnation.