多次实现相同的通用接口,包括具有不同类型参数的属性

时间:2017-01-16 10:09:07

标签: c# generics properties interface

从一开始,我就有了一个简单的通用界面:

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

一个多次实现它的类:

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; }
}

我无法将类型NiceItem和GreedyItem的ChosenItems公开,而且我也无法像这样访问它:

ChosenItem<GreedyItem> = new GreedyItem();

cuz'我有一个错误:

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

无论如何以这种方式使用这些道具,或者我已经把它弄错了,应该用词典或其他方式来做?

4 个答案:

答案 0 :(得分:2)

如果您希望保留通用IItemContainer,可以像这样实现GetChosenItem和SetChosenItem方法。

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;
    }
}

这与你想要做的非常接近。

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

答案 1 :(得分:2)

你的是一个案例显式接口实现。您为冲突的项目指定唯一名称,并将项目转发到界面。这也避免了任何命名混淆:

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;
        }
    }
}

作业很简单:

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

如果您在后台有更复杂的转换逻辑(例如,您指定了FaboulousItem并需要将其转换为NiceItem),则可以通过为您提供getter和/或setter来实现公共财产。

答案 2 :(得分:1)

这是完全错误的。您应该知道,您不能为成员项(属性,字段等)创建相同的名称。它会混淆编译器。

我建议修改你的界面:

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

现在在您的实施中:

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

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

此方法可让您存储源自Item的不同对象,并使用ChosenItem<T>()方法返回所需的对象。

修改

  

我有另一个操作项目列表的界面,因为某些子模块只能在一个项目上运行,而一些子模块只在一个项目上运行。我还需要独立存储每个实现类型的实例。

您可以随时使用工厂集合之类的东西(不知道名称是否正确)。

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;
    }
}

然后在您的界面而不是List<Item> ChosenItems,您可以ChosenItemCollection ChosenItems

在你的例子中使用它:

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

答案 3 :(得分:1)

我认为Pidon有一个很好的解决方案。但是在使用未实现的Item派生时可能会导致运行时错误。

另一个解决方案可能是添加属性,这些属性将对已实现的类型执行转换:

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();

这种方式每个实现的类型都有自己的ChosenItem实例。我认为这是一个干净的解决方案,而不会在代码中混淆泛型<T>