views:

101

answers:

3

Hi,

So i have such code.

public interface IGeoDataSet<out T> : IDataSet
    where T : IGeoPrimitive<IGeoPrimitiveContent>
{
    IEnumerable<T> Items { get; } 
}

public interface IDataSet { }

public interface IGeoPrimitive<T> : IPrimitive
    where T : IGeoPrimitiveContent
{
    T Content { get; }
}

public interface IPrimitive { }

public interface IGeoPrimitiveContent { }

And such inplementation for previous interfaces.

public class TriangleDataSet : IGeoDataSet<Triangle>
{
    public IEnumerable<Triangle> Items { get; private set; }
}

public class Triangle : IGeoPrimitive<TriangleContent>
{
    public TriangleContent Content { get; private set; }
}

public class TriangleContent : IGeoPrimitiveContent { }

When I try to compile this code I've got an error:

The type '.Triangle' cannot be used as type parameter 'T' in the generic type or method '.IGeoDataSet<T>'. There is no implicit reference conversion from '.Triangle' to '.IGeoPrimitive<.IGeoPrimitiveContent>'.

I can't understand why, maybe someone knows what is the problem?

Br, Jevgenij

A: 

You have to declare IGeoPrimitive<T> covariant:

public interface IGeoPrimitive<out T> : IPrimitive
    where T : IGeoPrimitiveContent
{
    T Content { get; }
}
Femaref
I've already try this solution, when I try to declare IGeoPrimitive<in T> covariant, I've got an error.
Jevgenij Nekrasov
yeah, that's because it's the opposite - contravariant.
Femaref
Invalid variance: The type parameter 'T' must be covariantly valid on '.IGeoPrimitive<T>.Content'. 'T' is contravariant.
Jevgenij Nekrasov
what is the best solution?
Jevgenij Nekrasov
@Femaref: You've got it backwards. It needs to be `IGeoPrimitive<out T>` (as you have it), which is **covariant**.
Dan Tao
Nope, the definition is contravariant, but it's covariant to IGeoPrimitive.
Femaref
@Femaref: What does that mean?
Dan Tao
you are right. mixed it up, I still have problems with it :)
Femaref
A: 

Are you better off declaring your interfaces as:

public interface IGeoDataSet<out T, out V> : IDataSet
    where T : IGeoPrimitive<V>
    where V : IGeoPrimitiveContent
{
    IEnumerable<T> Items { get; } 
}

and

public interface IGeoPrimitive<out T> : IPrimitive
    where T : IGeoPrimitiveContent
{
    T Content { get; }
}
FacticiusVir
+1  A: 

You need for your IGeoPrimitive<T> interface to also be covariant in order to be able to use Triangle as the type parameter for a IGeoDataSet<out T> implementation:

// Note addition of 'out' keyword
public interface IGeoPrimitive<out T> : IPrimitive
    where T : IGeoPrimitiveContent
{
    T Content { get; }
}

This is because you have a where T : IGeoPrimitive<IGeoPrimitiveContent> constraint; but without IGeoPrimitive<T> being covariant, a Triangle (which implements IGeoPrimitive<TriangleContent>) does not meet this constraint.

Dan Tao
yeap, it works, thanx :) but one more question, for example, i want to use ObservableCollection in IGeoDataSet for Items collection insted of IEnumerable? is it possible? when i've tried this i've got an error.
Jevgenij Nekrasov
@Jevgenij: No, I don't believe that will be possible, because `ObservableCollection<T>` is not covariant like `IEnumerable<T>`. This is because it has methods that take `T` as a parameter; for example, `Add`. If you tried to make this covariant you might have, say, a `TriangleDataSet`, cast it to an `IGeoDataSet<IGeoPrimitive<IGeoPrimitiveContent>>`, and then call `Items.Add(new Pentagon())`, which would clearly be illegal for a `TriangleDataSet`. Does that make sense?
Dan Tao
yeap, thanks :) but i do not understand your idea about private set accessor in Triangle type, i works fine with this accessor too.
Jevgenij Nekrasov
@Jevgenij: Yeah, I wasn't thinking straight; since the `set` is private, it really doesn't affect the type's covariance. I'll remove that part from my answer as it is just flat-out incorrect.
Dan Tao