带有IDictionary和IEnumerable的嵌套泛型

Hen*_*ger 4 c#

在内部可重用库的通用C#类中,我想传递一个引用"映射到其他东西列表的东西".那里传递的内容的数据类型不应该被库知道.此外,它们的存储方式也不应该是已知的,即今天的内存中保存的列表,以后可能是从按需读取的数据库表.

所以我以为我会写这个库类:

class GenericClass<T, U>
{
    public void Foo(IDictionary<T, IEnumerable<U>> bar)
    {
        // do something
    }
}
Run Code Online (Sandbox Code Playgroud)

这会编译,但尝试传递具体实现不会:

class UsingClass
{
    public static void Main(string[] args)
    {
        var c = new GenericClass<string, string>();
        c.Foo(new Dictionary<string, List<string>>());
    }
}
Run Code Online (Sandbox Code Playgroud)

我收到以下两个语法错误:

Filename.cs(46,13): error CS1502: The best overloaded method match for 'GenericClass<string,string>.Foo(System.Collections.Generic.IDictionary<string,System.Collections.Generic.IEnumerable<string>>)' has some invalid arguments
Filename.cs(46,19): error CS1503: Argument 1: cannot convert from 'System.Collections.Generic.Dictionary<string,System.Collections.Generic.List<string>>' to 'System.Collections.Generic.IDictionary<string,System.Collections.Generic.IEnumerable<string>>'
Run Code Online (Sandbox Code Playgroud)

替换IEnumerable声明Foo()并List修复它,但这当然不是我想要的.

这真的不是C#(4.0)支持的,还是我错过了一些明显的东西?你会建议什么解决方法?(我确信之前已经讨论了很多,所以链接到很好的描述也很好.)

是的,我应该能够为此编写自己的帮助程序类,但为什么我必须这样做?

Jon*_*eet 8

是的,这确实不受支持.想象一下你的Foo方法看起来像这样:

public void Foo(IDictionary<T, IEnumerable<U>> bar)
{
    T key = GetKeyFromSomewhere();
    bar[key] = new U[10]; // Create an array
}
Run Code Online (Sandbox Code Playgroud)

看起来没关系,不是吗?我们可以转换U[]为IEnumerable<U>.

从调用者的角度来看,它并不是那么好 - 突然我们string[]在字典中有一个参考值,当所有的值都是List<string>参考时!邦去了类型安全.

您可以将方法重写为:

public void Foo<TValue>(IDictionary<T, TValue> bar)
    where TValue : IEnumerable<U>
Run Code Online (Sandbox Code Playgroud)

这将让你值了字典,并把它们转换成IEnumerable<U>隐...但你只能够把完全正确类型的值到字典中,你可以不建,只是从一个U值.

从版本4开始,C#支持在受限情况下的通用差异.例如,这适用于C#4(当针对.NET 4时)但以前不会:

List<string> strings = new List<string>();
IEnumerable<object> objects = strings;
Run Code Online (Sandbox Code Playgroud)

对于很多更上通用的差异,看到埃里克利珀的博客系列的话题.做好准备让你的大脑定期爆炸.