使用一种泛型类型创建自定义可迭代字典

时间:2018-12-13 08:17:03

标签: c#

我想创建一个包含唯一的Type键的项“列表”,该键由项本身的类型作为键。我创建了一个包含Dictionary<Type, V>并对其进行管理的集合。

internal class TypeCollection<V>
{
    public TypeCollection()
    {
        items = new Dictionary<Type, V>();
    }

    private Dictionary<Type, V> items;

    public void Add<T>(T value) where T : V
    {
        items.Add(typeof(T), value);
    }

    public void Remove(Type type)
    {
        items.Remove(type);
    }

    public bool TryGetValue<T>(out T value) where T : V
    {
        if (items.TryGetValue(typeof(T), out V foundValue))
        {
            value = (T)foundValue;
            return true;
        }

        value = default(T);
        return false;
    }
}

我必须遍历这些值。 for-loop是不可能的,因为我必须按其类型访问值,但是foreach-loop可以完成工作。我实现了IEnumerable界面

TypeCollection<V> : IEnumerable<V>

并添加了必需的接口方法

    public IEnumerator<V> GetEnumerator()
    {
        foreach (V value in items.Values)
        {
            yield return value;
        }
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

当我想从该集合中删除所有值时,我必须实现这一点

    public void Clear()
    {
        items.Clear();
    }

您可能已经注意到我要重新发明一本词典,为什么要这么做...

我创建了这个

internal class TypeCollection<V> : Dictionary<Type, V>
{
    public void Add<T>(T value) where T : V
    {
        Add(typeof(T), value);
    }

    public bool TryGetValue<T>(out T value) where T : V
    {
        if (TryGetValue(typeof(T), out V foundValue))
        {
            value = (T)foundValue;
            return true;
        }

        value = default(T);
        return false;
    }
}

但是我无法覆盖默认的AddTryGetValue方法。我将同时拥有AddAdd<>这两种方法,那么“最干净”的方法是什么?我想隐藏默认的AddTryGetValue方法,因为不再需要使用它们。

4 个答案:

答案 0 :(得分:4)

您可以使用TypeCollection<TValue>中现有的KeyedByTypeCollection<TItem>,而不是创建自己的自定义System.Collections.Generic

  

KeyedByTypeCollection<TItem> Class

     

提供一个集合,该集合的项是用作键的类型。

     

备注

     

在集合中只允许每种类型的一个对象,因为类型是键,并且每个键必须唯一。但是您可以找到不同类型的对象。

但是,您可能需要对其进行子类化并将其扩展为包括方便的TryGetValue<T>(out T value),如下所示:

public class TypeCollection<V> : KeyedByTypeCollection<V>
{
    public T ValueOrDefault<T>() where T : V
    {
        if (!Contains(typeof(T)))
        {
            return default(T);
        }
        return (T)this[typeof(T)];
    }

    public bool TryGetValue<T>(out T value) where T : V
    {
        if (!Contains(typeof(T)))
        {
            value = default(T);
            return false;
        }

        value = (T)this[typeof(T)];
        return true;
    }
}

这是因为KeyedByTypeCollection<V>.Find<T>方法返回指定类型T 集合中的第一项,因此,如果您具有复杂的多态类型层次结构,则可能存在基本类型时,返回派生类型的实例:

var dictionary = new KeyedByTypeCollection<object>();

dictionary.Add("hello");
dictionary.Add(new object());

Assert.IsTrue(dictionary.Find<object>().GetType() == typeof(object)); // FAILS

有关更多使用示例,请参见 Uses of KeyedByTypeCollection in .Net?

答案 1 :(得分:1)

您可以重新引入这些方法并将其设为私有:

    private new void Add(Type key, V value)
    {

    }

    private new bool TryGetValue(Type key, out V value)
    {
        value = default(V);
        return false;
    }

答案 2 :(得分:1)

对于您的问题,有两种方法:继承和组合。 继承不允许您排除基类方法,因此Dictionary不是您类型的最佳基类。

在合成中没有这样的问题,因为您不愿公开什么方法。这是正常的方式。

解决方案:使用组合物或找到新的更好的基类。

答案 3 :(得分:0)

改为使用IDictionary界面:

internal class TypeCollection<V> : IDictionary<Type, V>
{
    protected readonly Dictionary _innerDictionary = new Dictionary<Type,V>();

}

一旦键入那么多,Visual Studio就会在代码的下划线处显示错误,并提醒您尚未实现该接口。右键单击错误,然后选择“通过_innerDictionary实现接口”,它将自动生成将方法连接到_innerDictionary所需的所有内容。然后,您可以修改任何内容。