Generics: Почему в этом случае компилятор не может определить аргументы типа? - PullRequest
8 голосов
/ 19 октября 2010

Я хотел написать метод расширения, который работал бы со словарями, значения которых были какой-то последовательностью. К сожалению, компилятор не может вывести общие аргументы из моего использования метода; Мне нужно указать их явно.

public static void SomeMethod<TKey, TUnderlyingValue, TValue>
    (this IDictionary<TKey, TValue> dict)
    where TValue : IEnumerable<TUnderlyingValue> { }    

static void Usage()
{
    var dict = new Dictionary<int, string[]>();
    var dict2 = new Dictionary<int, IEnumerable<string>>();

    //These don't compile
    dict.SomeMethod();
    SomeMethod(dict); // doesn't have anything to do with extension-methods
    dict2.SomeMethod(); // hoped this would be easier to infer but no joy


    //These work fine
    dict.SomeMethod<int, string, string[]>();
    dict2.SomeMethod<int, string, IEnumerable<string>>();
}

Я понимаю, что вывод типа не является точной наукой, но мне было интересно, есть ли здесь какое-то фундаментальное «правило», которое я здесь упускаю - я не знаком с деталями спецификации.

  1. Является ли это недостатком процесса логического вывода или я ожидаю, что компилятор должен «разобраться» в этом случае необоснованно (возможно, двусмысленность)?
  2. Могу ли я изменить сигнатуру метода таким образом, чтобы он был в равной степени функциональным и в то же время "выводимым"?

Ответы [ 3 ]

15 голосов
/ 19 октября 2010

Я понимаю, что вывод типа не является точной наукой

Я не уверен, что согласен.Спецификация довольно подробная.

Мне было интересно, есть ли здесь какое-то фундаментальное «правило», которого я здесь упускаю

Фундаментальное правило, которое вы упускаете, - это, вероятно,ограничения не являются частью подписи.Вывод типа работает на основе подписи.

На мой взгляд, существуют веские причины для такого решения.Тем не менее, многие люди считают, что я морально неправ, полагая, что есть веские причины для такого дизайнерского решения.Если вам интересно прочесть несколько миллионов слов на тему, прав я или нет, посмотрите мою статью на эту тему и сотни или около того комментариев о том, что я неправ:

http://blogs.msdn.com/b/ericlippert/archive/2009/12/10/constraints-are-not-part-of-the-signature.aspx

Является ли это недостатком процесса вывода?

Возможно, да.На мой взгляд, это разумный выбор, учитывая конкурирующие требования дизайна.(«Делать то, что имел в виду пользователь» и «выдавать ошибки, когда вещи выглядят неоднозначно».)

- это мое ожидание, что компилятор должен «выяснить это» в этом случае необоснованно?

Нет.Вы кажетесь разумным человеком, и ваши ожидания, кажется, основаны на веских доводах.Однако вполне возможно иметь разумное ожидание, которое, тем не менее, не выполнено.Это может быть одним из таких случаев.

Могу ли я изменить сигнатуру метода таким образом, чтобы сделать его в равной степени функциональным и в то же время «выводимым»?

Это будет сложно, поскольку универсальный тип словаря не является ковариантным или контравариантным в своих преобразованиях.Понятие, которое вы хотите уловить, нелегко выразить в системе типов способом, который позволяет сделать вывод.

Если вы предпочитаете использовать языки с более сложным выводом типов, подумайте об использовании F #.Если вы предпочитаете языки, которые склоняются к «делать то, что имел в виду пользователь», а не «сообщать об ошибках по неоднозначности», рассмотрите возможность использования VB.

4 голосов
/ 19 октября 2010

Вывод типа C # не работает без ограничений или возвращаемых значений. Таким образом, у вас будет немного удачи с

public static void SomeMethod<TKey, TUnderlyingValue>
    (this IDictionary<TKey, IEnumerable<TUnderlyingValue>> dict)
  { }

Это будет работать, если вы объявите параметр как new Dictionary< string, IEnumerable<int>>(), но не , если вы объявите его new Dictionary<string, List<int>>().

Я должен сказать, что, как я читаю раздел 7.5.2 c # spec , кажется, что, поскольку List<int> реализует IEnumerable<int>, вывод типа TUnderlyingValue должен работать , Однако этот раздел не совсем прост для понимания. Я предполагаю, что он не работает через несколько «слоев», так как SomeMethod<T>(IEnumberable<T> val){} будет прекрасно работать, вызывая его с SomeMethod(new List<string>()). Я специально не вижу в спецификации ничего, что касается разрешения типа, где U = Ca<Va, Cb<Vb>>, поэтому, возможно, логический вывод на этом уровне не определен.

1 голос
/ 19 октября 2010

Почему бы не указать тип IEnumerable?

public static void SomeMethod<TKey, TValue>
(this IDictionary<TKey, TValue> dict)
where TValue : IEnumerable { }    
...