簡體   English   中英

多次實現包含具有不同類型參數屬性的同一個通用接口

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

從一開始,我就擁有一個簡單的通用接口:

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

因為我有一個錯誤:

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

無論如何,以這種方式使用這些道具還是我弄錯了,應該用Dictionary或其他方式來做?

當您希望保留通用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());

您的情況是為實現顯式接口而設計的。 您給沖突的項目指定一個唯一的名稱,然后將項目轉發到界面。 這也避免了任何命名混亂:

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方法來實現。

這是完全錯誤的。 如您所知,您不能為成員項目(屬性,字段等)使用相同的名稱。 它將混淆編譯器。

我建議修改您的界面,例如:

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

我認為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>混亂。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM