Вывод общего типа не работает с динамическим? - PullRequest
4 голосов
/ 31 октября 2011

Я недавно играл с Massive, Micro-ORM, который возвращает коллекции IEnumerable .

Я обнаружил неожиданную проблему, когда попытался запросить одну из этих коллекций с помощью LINQ.

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

dynamic dynamicString = "d"; // just using a string here for simplicity, same problem occurs with any other type
string explicitString = string.Format("string is {0}", dynamicString); // works without issues

... кажется, что это невозможно сделать в следующем сценарии:

IEnumerable<string> strings = new[] { "a", "b", "c" };
IEnumerable<dynamic> dynamics = strings;

IEnumerable<string> output = dynamics.Select(d => string.Format("string is {0}", d)); // compiler error on this line

Компилятор жалуется:

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

Поскольку компилятор должен иметь возможность делать вывод, что мое лямбда-выражение возвращает строку, я ожидал бы, что он также сделает вывод, что TResult для выбора должен иметь тип string (а не динамический).

Это было легко исправить, указав TSource и TResult явно следующим образом:

IEnumerable<string> output2 = dynamics.Select<dynamic, string>(d => string.Format("string is {0}", d)); // works !!!

Или я мог бы присвоить результат IEnumerable ...

IEnumerable<dynamic> output3 = dynamics.Select(d => string.Format("string is {0}", d)); // also works

Я также подтвердил, что эта проблема не возникает, когда я заменяю свой IEnumerable на IEnumerable :

IEnumerable<object> objects = strings;
IEnumerable<string> output4 = objects.Select(o => string.Format("string is {0}", o)); // works

И что интересно, даже следующие работы:

IEnumerable<string> output5 = dynamics.Select(d => string.Format("string is {0}", (object)d)); // works
IEnumerable<string> output6 = dynamics.Select(d => string.Format("string is {0}", (string)d)); // works

Кто-нибудь может объяснить, что здесь происходит? Это известное ограничение компилятора C # или я нашел еще одну ошибку?

Ответы [ 2 ]

7 голосов
/ 31 октября 2011

Вам необходимо:

IEnumerable<string> output = dynamics.Select(d => (string)string.Format(
       "string is {0}", d));

Невозможно определить тип возвращаемого значения string, поскольку dynamic означает, что он должен предполагать, что возвращаемое значение равно dynamic, если имеетсяболее подходящая перегрузка string.Format для конкретного поставляемого типа (с другим типом возврата).Даже если мы знаем иначе, спецификации для dynamic не согласятся с нами; p Добавив явное приведение обратно к string, мы проясним тип возврата.

Лично я не вижу смысла для dynamic Вот;Вы также можете использовать object, тогда это не проблема:

IEnumerable<string> strings = new[] { "a", "b", "c" };
IEnumerable<object> dynamics = strings;

IEnumerable<string> output = dynamics.Select(d => string.Format(
      "string is {0}", d));

(или, действительно, оставьте как IEnumerable<string>) Я предполагаю, что у вас есть другая причина использовать dynamic, который не виден в этом примере.

0 голосов
/ 31 октября 2011

Я думаю, что проблема не связана с динамикой.У меня часто случается «ошибка ожидания пользователя», когда я надеюсь, что .Select <> выведет параметры универсального типа 1 .

Вы могли бы решить это следующим образом:

 Func<dynamic, string> selector = d => string.Format("string is {0}", d);
 IEnumerable<string> output = dynamics.Select(selector);

1 Я постараюсь добавить пример именно таких «удивительно неоднозначных» случаев, когда у меня есть время спустя

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...