Kamil Stadryniak
Kamil Stadryniak

Reputation: 632

Implement multiple times the same generic interface that includes properties with different type parameters

Well from the beginning, I've got a simple generic interface:

public interface IItemContainer<T> where T : Item
{
    T ChosenItem { get; set; }
}

And an class that implements it multiple times:

public class ChosenItemsContainer : IItemContainer<FabulousItem>, IItemContainer<NiceItem>, IItemContainer<GreedyItem>
{

    public FabulousItem ChosenItem { get; set; }
    NiceItem IItemContainer<NiceItem>.ChosenItem { get; set; }
    GreedyItem IItemContainer<GreedyItem>.ChosenItem { get; set; }
}

I can't make the ChosenItems of types NiceItem and GreedyItem public, and also I can't access it like this:

ChosenItem<GreedyItem> = new GreedyItem();

cuz' I've got an error:

'GreedyItem' is a type, which is not valid in the current context

Is there anyway to use those props in this manner or I've got it all wrong and should do It with Dictionary or other way?

Upvotes: 3

Views: 4823

Answers (4)

Sefe
Sefe

Reputation: 14007

Yours is a case explicit interface implementations were made for. You give your conflicting items a unique name and forward the items to the interface. That also avoids any naming confusions:

public class ChosenItemsContainer : IItemContainer<FabulousItem>, IItemContainer<NiceItem>, IItemContainer<GreedyItem> {

    public FabulousItem ChosenFabulousItem { get; set; }
    public NiceItem ChosenNiceItem { get; set; }
    public GreedyItem ChosenGreedyItem { get; set; }

    FabulousItem IItemContainer<FabulousItem>.ChosenItem {
        get {
            return ChosenFabulousItem;
        }
        set {
            ChosenFabulousItem = value;
        }
    }
    NiceItem IItemContainer<NiceItem>.ChosenItem {
        get {
            return ChosenNiceItem;
        }
        set {
            ChosenNiceItem = value;
        }
    }
    GreedyItem IItemContainer<GreedyItem>.ChosenItem {
        get {
            return ChosenGreedyItem;
        }
        set {
            ChosenGreedyItem = value;
        }
    }
}

Assignment is simple:

container.ChosenFabulousItem = new FabulousItem();
container.ChosenNiceItem = new NiceItem();
container.ChosenGreedyItem = new GreedyItem();

If you have more complex conversion logic in the background (e.g. you assign a FaboulousItem and need to convert it into a NiceItem), you can do so by providing getters and/or setters for your public properties.

Upvotes: 3

Jeroen van Langen
Jeroen van Langen

Reputation: 22073

I think Pidon has a nice solution. But could result in a runtime error when using not implemented Item derives.

Another solution could be adding properties which will do the casts to the implemented types:

public class ChosenItemsContainer : IItemContainer<FabulousItem>, IItemContainer<NiceItem>, IItemContainer<GreedyItem>
{

    // these properties are only visible when casting to the correct
    // interface. Which the public properties below will do.
    FabulousItem IItemContainer<FabulousItem>.ChosenItem { get; set; }
    GreedyItem IItemContainer<GreedyItem>.ChosenItem { get; set; }
    NiceItem IItemContainer<NiceItem>.ChosenItem { get; set; }



    // return this as IItemContainer<FabulousItem>
    public IItemContainer<FabulousItem> AsFabulous
    {
        get
        {
            return (IItemContainer<FabulousItem>)this;
        }
    }

    // return this as IItemContainer<NiceItem>
    public IItemContainer<NiceItem> AsNice
    {
        get
        {
            return (IItemContainer<NiceItem>)this;
        }
    }

    // return this as IItemContainer<GreedyItem>
    public IItemContainer<GreedyItem> AsGreedy
    {
        get
        {
            return (IItemContainer<GreedyItem>)this;
        }
    }
}

ChosenItemsContainer container = new ChosenItemsContainer();

container.AsFabulous.ChosenItem = new FabulousItem();
container.AsNice.ChosenItem = new NiceItem();
container.AsGreedy.ChosenItem = new GreedyItem();

This way each implemented type has it's own ChosenItem instance. I think this is a clean solution without cluttering of generic <T> in code.

Upvotes: 1

mrogal.ski
mrogal.ski

Reputation: 5930

It's completely wrong. As you should know you cannot make same names for member items (properties, fields etc. ). It will confuse compiler.

I would suggest to modify your interface a bit like :

public interface IItemContainer
{
    List<Item> ChosenItems { get; set; }
    T ChosenItem<T>() where T : Item;
}

Now in your implementation :

public class ItemContainer : IItemContainer
{
    IItemContainer.ChosenItems
    {
        get { // your implementation
        }
        set { // your implementation
        }
    }

    T IItemContainer.ChosenItem<T>()
    {
        return ((IItemContainer)this).ChosenItems.OfType<T>().FirstOrDefault();
    }
}

This method will let you store different objects that derive from Item and return desired one using ChosenItem<T>() method.

EDIT:

I've got another interface which operates on List of Items, cuz' some submodules works only on one Item, and some only on set. I need also to store an instance of each implemented type independently.

You can always use something like a factory collection ( dont know if the name is correct ).

public class ChosenItemCollection 
{
    Dictionary<Type, Item> _fac = new Dictionary<Type, Item>();

    public T Add<T>(T item) where T : Item
    {
        if(!_fac.ContainsKey(typeof(T))
        {
             _fac.Add(typeof(T), item);
        }
        else
        {
            _fac[typeof(T)] = item;
        }
    }

    public T GetChosenItem<T>() where T : Item
    {
        if(_fac.ContainsKey(typeof(T))
            return _fac[typeof(T)];

        return null;
    }
}

Then in your interface instead of List<Item> ChosenItems you can do ChosenItemCollection ChosenItems.

Using this in your example :

GreedyItem item = // ... 
ItemContainer.ChosenItems.Add(item);
ItemContainer.ChosenItem.ChosenItem<GreedyItem>();

Upvotes: 1

Pidon
Pidon

Reputation: 275

When you like to keep your generic IItemContainer you can implement a GetChosenItem and SetChosenItem method like this.

public class ChosenItemsContainer : IItemContainer<FabulousItem>, IItemContainer<NiceItem>, IItemContainer<GreedyItem>
{
    FabulousItem IItemContainer<FabulousItem>.ChosenItem { get; set; }
    NiceItem IItemContainer<NiceItem>.ChosenItem { get; set; }
    GreedyItem IItemContainer<GreedyItem>.ChosenItem { get; set; }

    public T GetChosenItem<T>()
        where T : Item
    {
        return ((IItemContainer<T>)this).ChosenItem;
    }

    public void SetChosenItem<T>(T value)
        where T : Item
    {
        ((IItemContainer<T>)this).ChosenItem = value;
    }
}

Which comes very close to what you were trying to do.

container.SetChosenItem<NiceItem>(new NiceItem());

Upvotes: 4

Related Questions