如何制作一个所有实现通用界面的类型安全包?

时间:2009-03-19 21:35:58

标签: .net generics

需要一个类型安全的商品袋,这些商品都实现了通用界面。

希望做的事情如下:

var stringItem = new IItem<string>();
var numberItem = new IItem<int>();
var items = new List<IItem<T>>(); //T of course doesn't accomplish what I want

items.Add(stringItem);
items.Add(numberItem);

类似的东西:

interface IItem
{
   object Value { get; set; }
}

//Update: 2009-03-19 03:08 PM MST
//Added the following interface for clarity of my question

interface IItem<T> : IItem
{
   new T Value { get; set; }
}

然后,我可以:

var items = new List<IItem>();

但是,我的包里丢失了安全性。所以,我想到了Dictionary

var dict = new Dictionary<Type, List<IItem<T>>>(); //T is wrong again

dict.Add(typeof(string), new List<IItem<string>>); //that sure looks nice

5 个答案:

答案 0 :(得分:5)

我认为你不能逃避IItem<int>IItem<string>不同的事实;通常的方法是基础接口:

interface IItem {
   object Value {get;}
}
interface IItem<T> : IItem {
   new T Value {get;}
}

这样,您针对IItem进行编码,但实际的实例(通常为某些IItem<T>实现T)在内部是强类型的。

答案 1 :(得分:1)

查看PolyDictionary实施here

class Key<T> { public Key() { } }

class PolyDictionary {
    private Dictionary<object, object> _table;

    public PolyDictionary() {
        _table = new Dictionary<object, object>();
    }

    public void Add<T>(Key<T> key, T value) {
        _table.Add(key, value);
    }

    public bool Contains<T>(Key<T> key) {
        return _table.ContainsKey(key);
    }

    public void Remove<T>(Key<T> key) {
        _table.Remove(key);
    }

    public bool TryGetValue<T>(Key<T> key, out T value) {
        object objValue;
        if (_table.TryGetValue(key, out objValue)) {
            value = (T)objValue;
            return true;
        }
        value = default(T);
        return false;
    }

    public T Get<T>(Key<T> key) {
        T value;
        if (!TryGetValue(key, out value))
            throw new KeyNotFoundException();
        return value;
    }

    public void Set<T>(Key<T> key, T value) {
        _table[key] = value;
    }
}

答案 2 :(得分:1)

你应该能够做出类似以下的事情

interface IItem { object Value {get; set;}}

interface IItem<T> : IItem { T Value {get; set;}}

var items = new List<IItem>();

items.add(new IItem<string>());
items.add(new IItem<int>());

但是当你拔出它时,你仍然需要进行一些施法。

答案 3 :(得分:0)

创建一个新类,其唯一目的是装箱您感兴趣的值。   - 此类应接受StringItem或NumberItem。   - 该类应允许您访问字符串或数字项成员。

当您将对象添加到列表中时,您需要将对象装箱,然后在再次取出时将其取消装箱。

如果这是C ++,那么这个设计的开销不应该超过额外的指针取消引用,这取决于你如何实现它,但是这里的关键短语是“装箱”和“拆箱”。

答案 4 :(得分:0)

public class GrabBag<T>
{
    private ArrayList inner = new ArrayList();

    public void Add(T item)
    {
        inner.Add(item);
    }

    public IEnumerable<U> Get<U>() where U : T
    {
        return inner.OfType<U>();
    }
}

或者,您可以使用通用List并调用OfType。这更有意义。