Janusz-kun
Janusz-kun

Reputation: 89

Generic C# class with limited types to some interface is not implementing other interface with member of the first one

I'got a problem with understanding why having such class:

public class Section<T> : ISection 
    where T : ISectionItem
{
    public string Title { get; set; }
    public LinkedList<T> Items { get; set; }
}

implementing such interface:

public interface ISection
{
    string Title { get; set; }
    LinkedList<ISectionItem> Items { get; set; }
}

I get compiler error saying:

Error   1   
'JobTracker.Model.CV.Section<T>' does not implement interface member 
'JobTracker.Model.CV.ISection.Items'. 
'JobTracker.Model.CV.Section<T>.Items' cannot implement 
'JobTracker.Model.CV.ISection.Items' 
because it does not have the matching return type of
'System.Collections.Generic.IEnumerable<JobTracker.Model.CV.ISectionItem>'.

It should be possible, as T is guaranteed to be of type ISectionItem. One of possible solutions problably would be to create addidiotional to or replace ISection with generic ISection<T>, but I dont know how it will behave when I will want to use it with Xaml. Is there any to let know the compiler that the interface meets the requirements?

EDIT1: Changed IEnumerable to LinkedList in ISection as it was a bad concept and answers focus a bit aside from topic I was the most interested in.

EDIT2: My question was more directed on why T implementing ISectionItem is not allowed, but I noticed how stupid I was. Interface ISection allows usage of several different classes implementing ISectionItem in one list Section<T> does not.

Guys, both Your answers are correct but I will mark Olivier's as an answer as it alos shows the solution to this problem.

Upvotes: 0

Views: 593

Answers (3)

Olivier Jacot-Descombes
Olivier Jacot-Descombes

Reputation: 112752

If you access Items through the interface it would be possible to assign it a collection not being a LinkedList<T>, which would result in an error. Since Items also has a setter, it cannot be anything but IEnumerable<T>; however, you can implement it like this

private LinkedList<T> _items;
public IEnumerable<T> Items {
    get { return _items; }
    set {
        var linkedList = value as LinkedList<T>;
        if (linkedList != null) {
            _items = linkedList;
        } else {
            _items = new LinkedList<T>(value);
        }
    }
}

For this to work, you have to change the interface to:

public interface ISection<T>
    where T : ISectionItem
{
    string Title { get; set; }
    IEnumerable<T> Items { get; set; }
}

and to declare the class like this

public class Section<T> : ISection<T> 
    where T : ISectionItem
{
    ...
}

Your edit does not solve the problem. Through your class you can assign a LinkedList<T> of any T implementing ISectionItem; however your interface allows you to add a ISectionItem different from T.

class DerivedA : ISectionItem
{
    ...
}

class DerivedB : ISectionItem
{
    ...
}
ISection s = new Section<DerivedA>();
s.AddLast(new DerivedB()); // The collection in `s` is a `LinkedList<DerivedA>` boom!

The moral of the story is that the inheritance of the collection items (or in general: of the generic type parameters of some type) does NOT define the inheritance of the collection.

Let's T' derive from T.

Collection<T'> derives from Collection<T> ==> false!

The two collections are just two different collections not having any assignment compatibility.

Upvotes: 2

D Stanley
D Stanley

Reputation: 152644

If you could make your Items interface property read-only, the simplest route seems to be to add an explicit interface implementation as an overload:

public interface ISection
{
    string Title { get; set; }
    IEnumerable<ISectionItem> Items { get; }
}

public class Section<T> : ISection 
    where T : ISectionItem
{
    public string Title { get; set; }
    public LinkedList<T> Items { get; set; }

    IEnumerable<ISectionItem> ISection.Items
    {
        get {return Items.Cast<ISectionItem>();}
    }
}

Upvotes: 0

Ben Voigt
Ben Voigt

Reputation: 283911

It doesn't meet the requirements.

Consider the following

Section<ItemX> xSection = new Section<ItemX>;
//xSection.Items = BindingList<ISectionItem>();
ISection xISection = xSection;
xISection.Items = BindingList<ISectionItem>();

The contract from the interface is that Items can be assigned to any collection of ISectionItem.

Upvotes: 2

Related Questions