Как я могу сделать безопасную сумку типа вещей, которые все реализуют общий интерфейс? - PullRequest
2 голосов
/ 20 марта 2009

Необходимо иметь типобезопасную сумку предметов, которые реализуют общий интерфейс.

Желание сделать что-то вроде:

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 ]

5 голосов
/ 20 марта 2009

Я не думаю, что вы можете избежать того факта, что IItem<int> и IItem<string> различны; обычный подход - базовый интерфейс:

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

Таким образом, вы кодируете против IItem, но фактические экземпляры (которые обычно реализуют IItem<T> для некоторых T) внутренне строго типизированы.

1 голос
/ 20 марта 2009

Вы должны быть в состоянии сделать что-то вроде следующего

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

Но вам все равно придется выполнять кастинг, когда вынимаете его.

1 голос
/ 20 марта 2009

Ознакомьтесь с реализацией PolyDictionary здесь .

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;
    }
}
0 голосов
/ 20 марта 2009
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. Это имеет больше смысла.

0 голосов
/ 20 марта 2009

Создайте новый класс, который имеет единственную цель - поместить интересующие вас значения. - Этот класс должен принимать либо StringItem, либо OR NumberItem. - Класс должен предоставлять вам доступ к элементу строки или числа.

Когда вы добавляете свои объекты в список, вам нужно будет упаковать свои объекты, а затем распаковать их, когда вы снова их удалите.

Если это был C ++, то накладные расходы этого проекта не должны быть больше, чем разыменование дополнительного указателя, в зависимости от того, как вы его реализуете, однако ключевыми словами здесь являются «бокс» и «распаковка».

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...