在C#中:如何声明类型为键的通用词典和IEnumerable<>那种类型的价值?

时间:2010-05-31 14:56:03

标签: c# generics dictionary types

我想声明一个字典,用于存储特定类型的类型IEnumerable,并将该确切类型作为键,如下所示:(编辑后跟随johny g的评论)

private IDictionary<Type, IEnumerable<T>> _dataOfType where T: BaseClass; //does not compile!

我想要存储的具体类,都来自BaseClass,因此想把它当作约束。编译器抱怨它希望在成员名之后加一个分号。

如果它可以工作,我希望这会使后来从字典中检索简单如下:

IEnumerable<ConcreteData> concreteData;
_sitesOfType.TryGetValue(typeof(ConcreteType), out concreteData);

如何定义这样的字典?

6 个答案:

答案 0 :(得分:25)

使用.Net框架中已有的System.ComponentModel.Design.ServiceContainer

        ServiceContainer container = new ServiceContainer();

        IList<int> integers = new List<int>();
        IList<string> strings = new List<string>();
        IList<double> doubles = new List<double>();

        container.AddService(typeof(IEnumerable<int>), integers);
        container.AddService(typeof(IEnumerable<string>), strings);
        container.AddService(typeof(IEnumerable<double>), doubles);

答案 1 :(得分:15)

您甚至可能不需要字典才能执行此操作 - 但这取决于您的需求。如果每个appdomain每个类型只需要1个这样的列表(即“词典”是static),则以下模式可以有效并且很好地促进类型推断:

interface IBase {}

static class Container {
    static class PerType<T> where T : IBase {
        public static IEnumerable<T> list;
    }

    public static IEnumerable<T> Get<T>() where T : IBase 
        => PerType<T>.list; 

    public static void Set<T>(IEnumerable<T> newlist) where T : IBase 
        => PerType<T>.list = newlist;

    public static IEnumerable<T> GetByExample<T>(T ignoredExample) where T : IBase 
        => Get<T>(); 
}

请注意,在采用此方法之前,您应该仔细考虑编译时类型和运行时类型之间的区别。此方法很乐意让您在IEnumerable<SomeType>下存储运行时类型的SomeType变量,如果您在SomeType的任何IBase基类型下投放它,则包括if既没有运行时也没有编译类型错误 - 这可能是一个功能,或者等待发生的错误,所以你可能需要{{1}}来检查它。

此外,这种方法忽略了线程;所以如果你想从多个线程访问这个数据结构,你可能想要添加一些锁定。引用读/写是原子的,因此如果你无法锁定,你就不会受到损害,但是当然可能存在过时的数据和竞争条件。

答案 2 :(得分:11)

您不会在私人会员中约束T;你在课堂上限制它。

class Foo<T> where T : BaseClass
{
    private IDictionary<T, IEnumerable<T>> _dataOfType;

    public Foo(IDictionary<T, IEnumerable<T>> dataOfType)
    {
        this._dataOfType = dataOfType;
    }   
}   

答案 3 :(得分:7)

  1. 您不能约束特定变量。它只适用于类和方法。说实话,它在变量级别上没有任何意义。
  2. 你想要的是一个自定义类 - class WeirdDictionary : IDictionary<Type, IEnumerable>,它将重载Add方法以获取Type和该类型的IEnumerable,你可以使用约束来做,并转换IEnumerable&lt;&gt;到IEnumerable。重载索引器,并将其转换回相关类型 所有这些演员都是必需的,因为泛型是严格的IEnumerable<Base>IEnumerable<Derived>一样好(我认为这称为方差?)
  3. 这个解决方案略有概括,因为重用岩石

    编辑280Z28:

    起初我把它标记下来,因为第2点令人困惑,我误解了它。通过在IDictionary<Type, IEnumerable>中使用方法的显式实现并提供通用替代方法,您可以获得非常干净的界面。请注意,您无法创建通用索引器,因此您必须始终使用TryGet<T>(无论如何这都是个好主意)。我只包括显式实现其中一个IDictionary<>方法来显示如何执行检查。 不要直接从WeirdDictionary派生Dictionary<Type, IEnumerable>,否则您将无法保证基础数据中的约束。

    class WeirdDictionary : IDictionary<Type, IEnumerable>
    {
        private readonly Dictionary<Type, IEnumerable> _data =
            new Dictionary<Type, IEnumerable>();
    
        public void Add<T>(IEnumerable<T> value)
        {
            _data.Add(typeof(T), value);
        }
    
        public bool TryGet<T>(out IEnumerable<T> value)
        {
            IEnumerable enumerable;
            if (_data.TryGetValue(typeof(T), out enumerable)
            {
                value = (IEnumerable<T>)enumerable;
                return true;
            }
    
            value = null;
            return false;
        }
    
        // use explicit implementation to discourage use of this method since
        // the manual type checking is much slower that the generic version above
        void IDictionary<Type, IEnumerable>.Add(Type key, IEnumerable value)
        {
            if (key == null)
                throw new ArgumentNullException("key");
            if (value != null && !typeof(IEnumerable<>).MakeGenericType(key).IsAssignableFrom(value.GetType()))
                throw new ArgumentException(string.Format("'value' does not implement IEnumerable<{0}>", key));
    
            _data.Add(key, value);
        }
    }
    

    结束280Z28

答案 4 :(得分:4)

制作自定义词典类:

public class BaseClassDictionary<T, IEnumerable<T>> : Dictionary<T, IEnumerable<T>>
    where T : BaseClass
{
}

然后您可以将此专用字典用作字段类型:

private BaseClassDictionary<BaseClassDerivedType, IEnumerable<BaseClassDerivedType>> myDictionary;

答案 5 :(得分:1)

试试这个:

public class MyCustomDictionary<T>: Dictionary<T, IEnumerable<T>>  { }