I've just run into what I think is an oddity in type-casting. I have code similar to the following:
interface IMyClass { }
class MyClass: IMyClass { }
class Main
{
void DoSomething(ICollection<IMyClass> theParameter) { }
HashSet<MyClass> FillMyClassSet()
{
//Do stuff
}
void Main()
{
HashSet<MyClass> classSet = FillMyClassSet();
DoSomething(classSet);
}
}
When it gets to DoSomething(classSet), the compiler complains that it can't cast HashSet<MyClass> to ICollection<IMyClass>. Why is that? HashSet implements ICollection, MyClass implements IMyClass, so why isn't the cast valid?
Incidentally this isn't hard to work around, thought it's slightly awkward.
void Main()
{
HashSet<MyClass> classSet = FillMyClassSet();
HashSet<IMyClass> interfaceSet = new HashSet<IMyClass>();
foreach(IMyClass item in classSet)
{
interfaceSet.Add(item);
}
DoSomething(interfaceSet);
}
To me, the fact that this works makes the inability to cast even more mysterious.
Best Answer
It won't work because all instances of
MyClass
beingIMyClass
doesn't automatically imply that all instances ofHashSet<MyClass>
are alsoHashSet<IMyClass>
. If it worked, you could:Technically, it doesn't work because C# (<= 3.0) generics are invariant. C# 4.0 introduces safe-covariance, which doesn't help in this case either. It does, however, help when interfaces use the type parameters only in input or only in output positions. For instance, you'll be able to pass a
HashSet<MyClass>
as anIEnumerable<IMyClass>
to some method.By the way, they are easier workarounds than manually filling another
HashSet
like:or you can use the
Cast
method if you want to cast a set to anIEnumerable<IMyClass>
: