Question

I have the following classes:

public class Item
{
}

public class ItemCollection<TItem> : ICollection<TItem> where TItem : Item, new()
{
}

I have two derived classes:

public class Feature : Item
{
}

public class Features : ItemCollection<Feature>
{
}

I have a manager class to manage such collections:

public class ItemCollectionManager<TCollection> where TCollection : ItemCollection<Item>, new()
{
}

I tried to use this class:

public class FeatureManager : ItemCollectionManager<Features>
{
}

but this results in:

"The type Features must be convertible to ItemCollection<Item> in order to use it as TCollection in the generic class ItemCollectionManager<TCollection>".

And as previously mentioned,

Features is-a ItemCollection<Feature>

and

Feature is-a Item.

I do not believe interfaces are the ideal solution to this task but am willing to change if reason is provided.

If anyone could advise on what I'm doing wrong it would be very much appreciated.

Thank you.

Was it helpful?

Solution

You cannot do the...

ItemCollection<Feature> features = ...;
ItemCollection<Item> items = features;

It's about generics variance (covariance and contravariance that is) - and it's only supported for interfaces, delegates - and only providing they're designed that way (decorated with in / out - and conforming to the rules that go with it). e.g. IEnumerable<> is (if you lookup its definition you'll see the out). For more details I think it's best to read more...

How is Generic Covariance & Contra-variance Implemented in C# 4.0?
Understanding Covariant and Contravariant interfaces in C#
http://msdn.microsoft.com/en-us/library/dd799517.aspx

In your case, you could design an IItemCollection<out T> (interface) - that (theoretically) might support the casting you need. But it'd have to be read-only (a tad simplified and not entirely correct but it's easier to think that way - the rules are a bit more complex).

Since you're naming it a 'collection' I'm assuming it's not just for enumerating, viewing the items - i.e. if you have an Add of a sort (which requires input parameters - i.e. contra-variance) that clashes with the 'covariance' you need for the upcasting. Also any other parameters involved might not allow your interface to be made covariant.


Also some other posts I made related...

How to make generic class that contains a Set of only its own type or subtypes as Children?

C# generic handlers, what am I misunderstanding?

OTHER TIPS

You need an additional generic argument on ItemCollectionManager, TItem.

I believe your ItemCollectionManager class definition should look something like this.

    public class ItemCollectionManager<TCollection, TItem> where TCollection : ItemCollection<TItem>, new(), 
where TItem : Item
    {
    }

The way you have the FeatureManager class defined right now it would be entirely acceptable to add ANY CLASS which inherits Item to the TCollection, since the only restriction on TCollection is that it contains classes of type Item. The collection Features accepts only Feature classes or classes which inherit the Feature type. Since you can't add the base type of Item to Features, it should not compile.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top