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
?