Markus
Markus

Reputation: 2281

Is there a way to avoid the Cast<T>() if I already have the correct type in the following C# scenario?

public class ItemStore {
    private Dictionary<Type, List<object>> _items = new Dictionary<Type, List<object>>();

    public void AddItem(object item) {
        var type = item.GetType();
        if (!_items.ContainsKey(type)) {
            _items[type] = new List<object>();
        }
        _items[type].Add(item);
    }

    public IEnumerable<T> GetItems<T>() {
        if(!_items.ContainsKey(typeof(T))) {
            return new List<T>();
        }
        return _items[typeof(T)].Cast<T>();
    }
}

(The real scenario is more complex, and it is a library used in multiple Projects knowing nothing about the concrete types.... )

The Cast<T>() in GetItems() is consuming quite some time. So I would prefere to avoid it. Is there any way to avoid it in C# - because actually the list already contains the correct types?

Upvotes: 2

Views: 112

Answers (3)

Oxald
Oxald

Reputation: 837

  • Use IList instead of List(T)
  • Make AddItem() method generic

    public class ItemStore
    {
      private Dictionary<Type, IList> _items = new Dictionary<Type, IList>();
    
      public void AddItem<T>(T item)
      {
          var type = typeof(T);
          if (!_items.ContainsKey(type))
          {
              _items[type] = new List<T>();
          }
          _items[type].Add(item);
      }
    
      public IEnumerable<T> GetItems<T>()
      {
          if (!_items.ContainsKey(typeof(T)))
          {
              return new List<T>();
          }
          return (List<T>)_items[typeof(T)];
      }
    }
    

Upvotes: 4

user2819245
user2819245

Reputation:

You could make your method AddItem generic, which would allow you to store List<T> instances in your dictionary (whose generic TValue parameter should be IList in this case).

public class ItemStore
{
    private Dictionary<Type, IList> _items = new Dictionary<Type, IList>();

    public void AddItem<T>(T item)
    {
        IList objList;
        if (!_items.TryGetValue(typeof(T), out objList))
        {
            objList = new List<T>();
            _items[typeof(T)] = objList;
        }
        objList.Add(item);
    }

    public IEnumerable<T> GetItems<T>()
    {
        IList objList;
        return
            (_items.TryGetValue(typeof(T), out objList)) ? (List<T>)objList
            : new List<T>();
    }
}

Upvotes: 2

Mike Zboray
Mike Zboray

Reputation: 40818

You need to modify the internal structure of this class a bit to not use generics in the items lookup because we need to underlying type of the stored list to be the correct type. This requires a little reflection when creating the list. We can also make AddItem and GetItems a little more efficient by avoiding extra lookups:

public class ItemStore {
    private Dictionary<Type, IList> _items = new Dictionary<Type, IList>();

    public void AddItem(object item) {
        var type = item.GetType();
        IList list;
        if (!_items.TryGetValue(type, out list)) {
            var listType = typeof(List<>).MakeGenericType(type);
            list = (IList)Activator.CreateInstance(listType);
            _items[type] = list;
        }

        list.Add(item);
    }

    public IEnumerable<T> GetItems<T>() {
        IList list;
        if(!_items.TryGetValue(typeof(T), out list)) {
            return Enumerable.Empty<T>();
        } else {
            return (IEnumerable<T>)list;
        }
    }
}

If you can modify the signature of AddItem this could be even simpler (no reflection), but given you've said this is an over simplification, I will leave the public API unchanged.

Upvotes: 7

Related Questions