LINQ: выбор в динамическом списке не компилируется - PullRequest
0 голосов
/ 24 мая 2018

Предположим, у меня есть такой метод:

static string ToString(dynamic d)
{
    return (string)d.ToString();
}

И, например, у меня есть var tmp = new List<dynamic> { 1, "2", 345 };

Почему

IEnumerable<string> test = tmp.Select(ToString);

компилируется нормально, но

IEnumerable<string> test = tmp.Select(x => ToString(x));

нет?

Ошибка:

CS0266  Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<dynamic>'  to 'System.Collections.Generic.IEnumerable<string>'. An explicit conversion exists (are you missing a cast?)

Это верно для VS 2015 и VS 2017 для всех> = 4.5.0 сред

Ответы [ 2 ]

0 голосов
/ 24 мая 2018

Я не буду повторять ответ Эд о "динамическом заражении".Но попытаемся пролить свет на причину успешной компиляции первого случая.

Здесь

IEnumerable<string> test = tmp.Select(ToString);

ToString - это метод группы - функция подслащивания, которая позволяет вам передать методв качестве делегата с соответствующей подписью, используя какой-то ярлык.Некоторая дополнительная информация может быть найдена в этой теме Что такое группа методов в c # .

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

Func<dynamic, string> toString = new Func<dynamic, string>(ToString);
IEnumerable<string> test = tmp.Select(toString);

Как вы можете видеть, у нас есть явный тип возвращаемого делегата, и он не динамический, поэтому мы не должны беспокоиться о динамическом заражении.

0 голосов
/ 24 мая 2018

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

Если навести указатель мыши на Select, он будет иметь тип Select<dynamic, string>, поэтому он вернет IEnumerable<String>.Поскольку ToString(dynamic d) имеет явное приведение к string в return и возвращает тип string, компилятор может быть уверен, что ToString действительно возвращает строку.

IEnumerable<string> test = tmp.Select(ToString);

Кстати, если мы внесем эти изменения в ToString (), вышеприведенное все равно будет скомпилировано, а лямбда-версия по-прежнему не будет:

    static string ToString(object d)
    {
        //  Remove the cast to string
        return d.ToString();
    }

Заражение возникает, когда мысоздать tmp как List<dynamic>.

В лямбда-версии, которая не компилируется, при наведении курсора мыши мы на самом деле вызываем Select<dynamic, dynamic>, который возвращает IEnumerable<dynamic>.

Разрешение перегрузки выполняется во время выполнения, когда вы передаете dynamic.Компилятор не может гарантировать во время компиляции, какой метод будет фактически вызван или что он вернет.Intellisense считает, что ToString - это ваш статический метод, но компилятор не верит, что он останется верным.

IEnumerable<string> test2 = tmp.Select(x => ToString(x));

Это компилируется, потому что у нас есть приведение, где разрешение перегрузки не повлияет на него.

IEnumerable<string> test3 = tmp.Select(x => (string)ToString(x));

Джонатон Чейз любезно отмечает в комментариях, что мы заставляем его компилировать, явно передавая параметры типа в Select:

IEnumerable<string> test4 = tmp.Select<dynamic, string>(x => ToString(x));

Важный вопрос, на мой взгляд,Вот почему ваш первый случай компилируется. Мое осторожное предположение состоит в том, что поскольку вы передаете ссылку на метод, а не вызываете его, динамическое разрешение перегрузки не происходит.

Считайте это заполнителем, пока кто-то с более глубоким пониманием не заинтересуется этим вопросом.

...