Поддерживает ли синтаксис запроса LINQ утиную печать?

Что касается синтаксиса запроса LINQ...

var foo = new List<int> { 1, 2 };

var boo = from n in foo
            where n > 1
            select n;

... Я всегда думал, что этот синтаксис ограничен работой с IEnumerable. По крайней мере, пока я не узнал об IQueryable. И, возможно, IObservable тоже. Но недавно я заметил предположение, что синтаксис запроса основан на утином вводе. Эта история не выглядела очень убедительной, пока я не нашел сайт, посвященный LINQ to Tasks. Похоже, что LINQ to Tasks полностью зависит от утиного ввода с синтаксисом запроса!

Хорошо, что здесь происходит? Синтаксис запроса использует утиную печать или нет? Когда я попробовал сам, конечно же, это работает и, кажется, доказывает, что все дело в утиной печати, а не в IEnumerable:

public class Joker<T>
{
    public T Item;

    public Joker(T item)
    {
        Item = item;
    }
}

public static class JokerHelp
{

    public static T2 Select<T,T2>(this Joker<T> joke, Func<T,T2> call)
    {
        return call(joke.Item);
    }
}

var oof = new Joker<int>(5);
int foo = from a in oof
          select a;

Если утиная типизация — это то, как работает синтаксис запроса, как это, очевидно, и происходит, где может быть официальная (MSDN) документация по этому поводу? Или любая разумная документация?


person Brent Arias    schedule 07.08.2013    source источник


Ответы (2)


В C# есть несколько функций, для которых компилятор выполняет сопоставление структурных типов, а не сопоставление номинальных типов. Примеры включают цикл foreach, синтаксис понимания запросов (select, where и т. д.) и await/async. Для всех этих функций компилятор на самом деле просто ищет методы с определенными именами, а не конкретные интерфейсы или классы.

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

Эрик Липперт гораздо более подробно объясняет функцию и аргументацию здесь.

Я заметил, что документация MSDN часто неправильный или неполный об этих функциях.

person MgSam    schedule 07.08.2013
comment
Прошло немало времени. Статья Эрика Липперта по-прежнему доступна здесь: blogs.msdn .microsoft.com/ericlippt/2011/06/30/ - person Corey; 19.09.2020

Чего вам не хватает, так это того, что List<T> реализует IEnumerable<T>. Таким образом, «я всегда думал, что этот синтаксис ограничен работой с IEnumerable» технически верно, хотя и в ограниченном виде. IQueryable также реализует IEnumerable вместе с IList и массивами. Таким образом, вы можете выполнять linq-запросы ко всему, что реализует IEnumerable.

Поскольку Joker<> не реализует IEnumerable<>, ваша попытка запроса не удастся. Методы расширения Select<>(), Where<>() и т. д. построены вокруг IEnumerable<>. Итак, если вы хотите выбрать из oof, вам просто нужно обновить определение Joker<>.

public class Joker<T> : IEnumerable<T>
{
  // (actually implement IEnumerable<T> functionality
}

(Редактировать: ответ действительно имел смысл в контексте первоначально отформатированного вопроса. Отредактированный вопрос делает мой ответ устаревшим)

person eouw0o83hf    schedule 07.08.2013
comment
Отредактировал мой пост - и код работает без участия IEnumerable. Иди разберись. - person Brent Arias; 08.08.2013
comment
Вау, как насчет этого. Может быть, компилятор получает супер-рефлексию во время выполнения и просто преобразует from a in oof в метод расширения Select для переданного типа, и в итоге он работает? Такого точно не ожидал. Очаровательный. - person eouw0o83hf; 08.08.2013
comment
Никакого отражения (во время выполнения)... просто старая обычная утиная печать. Прочитайте статью в Википедии, которую я связал. :) - person Brent Arias; 08.08.2013