Reputation: 41
In the implementation of GetEnumerator() below, the compiler refuses to convert List<T>.Enumerator
to IEnumerator<Shape>
even though T is constrained by Shape. (1) Why is this, and (2) is there a workaround I'm overlooking?
using System.Collections.Generic;
interface Shape {
void Draw();
}
class Picture<T> : IEnumerable<Shape> where T : Shape {
List<T> shapes;
public IEnumerator<Shape> GetEnumerator()
{
return shapes.GetEnumerator();
}
}
Upvotes: 3
Views: 69
Reputation: 21699
The compiler simply does not support this kind of type inference. If your types can be reference types, Ben's answer is the way to go. If not you're stuck with workarounds.
There are a few workarounds. Choose the one that makes sense for you.
public IEnumerator<Shape> GetEnumerator()
{
return shapes.Cast<Shape>().GetEnumerator();
}
Shape
. Drawback is that your implementation will have to cast back to T
at some point. Here's an example with some methods I've added.List<Shape> shapes = new List<Shape>();
public IEnumerator<Shape> GetEnumerator()
{
return shapes.GetEnumerator();
}
public void AddShape(T shape) => shapes.Add(shape);
public T GetShape() => (T)shapes.First();
Upvotes: 0
Reputation: 141600
Because variance does not work for value types. So if it is suitable for you can constrain T
to be a class:
private class Picture<T> : IEnumerable<Shape> where T : class, Shape
{
private List<T> shapes;
public IEnumerator<Shape> GetEnumerator()
{
return shapes.GetEnumerator();
}
}
Upvotes: 1
Reputation: 283624
Change the constraint from
where T : Shape
to
where T : class, Shape
Interface covariance doesn't work for value types.
Documented here:
Variance applies only to reference types; if you specify a value type for a variant type parameter, that type parameter is invariant for the resulting constructed type.
Upvotes: 6