Не удается преобразовать словарь в IDictionary в вызове метода
Я ни за что на свете не могу этого понять. Допустим, у меня есть следующие два объекта словаря:
// Assume "query" is a LINQ queryable.
Dictionary<string, int> d1 = query.ToDictionary(k => k.Key, v => v.Value);
Dictionary<string, int> d1 = query.ToDictionary(k => k.Key, v => v.Value);
Следующее утверждение приводит к ошибке времени компиляции, что неявное преобразование между словарем и IDictionary невозможно:
// Compile time error!
Tuple<IDictionary<string, int>, IDictionary<string, int>> = Tuple.Create(d1, d2);
Я должен выполнить преобразование явно:
Tuple<IDictionary<string, int>, IDictionary<string, int>> = Tuple.Create(d1 as IDictionary<string, int>, d2 as IDictionary<string, int>);
Я не понимаю, почему компилятор не может вычислить операцию ковариации-словарь реализует IDictionary - тем более, что что-то вроде этого будет курсовая работа, как мы все знаем:
IDictionary<string, int> d3 = d1;
Я уверен, что есть веская причина для такого поведения, и мне любопытно, что это такое.
Обновление 1: Просто чтобы уточнить, мне интересно поведение, а не то, как решить проблему. Я знаю о различных решениях :)
Обновление 2:
Спасибо всем за отличные ответы. Я не знал, что Tuple
инвариантно, и теперь знаю.
5 ответов:
В основном проблема заключается в том, что семейство
Tuple
не является ковариантным в своих аргументах типа. Этого не может быть, потому что это класс. Интерфейс или версия делегата может быть создана , которая была бы ковариантной, однако, поскольку нет никаких членов, принимающих параметры типа во входных позициях.Это проще всего увидеть с помощью
Tuple<T1>
:Tuple<string> stringTuple = Tuple.Create("Foo"); Tuple<object> objectTuple = stringTuple;
Этот вызов:
Tuple.Create(d1, d2);
... выводит оба аргумента типа как
Dictionary<string, int>
, поэтому вы пытаетесь преобразовать изTuple<Dictionary<string, int>, Dictionary<string, int>>
кTuple<IDictionary<string, int>, IDictionary<string, int>>
, который не работает.Версия с
as
изменяет типы аргументов так, что вывод типа дает желаемые аргументы типа - но было бы проще просто написать аргументы типа напрямую и полностью избежать вывода, Как в ответе Себастьяна:Tuple.Create<IDictionary<string, int>, IDictionary<string, int>>(d1, d2)
Если вы используете
var
в этот момент, это не так плохо:var x = Tuple.Create<IDictionary<string, int>, IDictionary<string, int>>(d1, d2);
Тип
x
теперь будетTuple<IDictionary<string, int>, IDictionary<string, int>>
, который вы хотите.EDIT: как отмечено в комментариях, вы также можете просто используйте конструктор в этой точке:
var x = new Tuple<IDictionary<string, int>, IDictionary<string, int>>(d1, d2);
Вы пытаетесь назначить...
Tuple<Dictionary<string, int>, Dictionary<string, int>>
... к ...
Tuple<IDictionary<string, int>, IDictionary<string, int>>
...но
Tuple<T1, T2>
является классом и поэтому инвариант.
Параметры типа Для Кортежа.Create выводятся из типов, и, таким образом, правая сторона является кортежем из двух словарей, и это другой тип, чем Кортеж IDictionarys - если вы явно добавляете параметры типа в кортеж.Create to be of type IDictionary все должно работать, как ожидалось, потому что это нормально для параметров метода, чтобы быть более определенного типа.
Tuple.Create<IDictionary<string, int>,IDictionary<string, int>>(d1,d2)
Нет никакого преобразования между a
Компилятор определил наиболее специфичный тип возвращаемого типа для вашего вызоваTuple<Dictionary<string, int>, Dictionary<string, int>>
и ATuple<IDictionary<string, int>, IDictionary<string, int>>
, так как классы в C# (и, следовательно, классTuple<T1, T2>
) не поддерживают ковариацию.Tuple.Create
и не использует тип слева во время вывода.