Does LINQ "Query Syntax" Support Duck Typing?
Asked Answered
S

2

0

Regarding LINQ query syntax...

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

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

...I always thought this syntax was limited to operating on IEnumerable. Or at least until I learned about IQueryable. And perhaps IObservable as well. But I recently noticed a suggestion that query syntax is based on duck typing. That story didn't look terribly convincing, until I found a site that is dedicated to LINQ to Tasks. LINQ to Tasks looks like it is wholly dependent on duck typing with query syntax!

Ok, what is going on here? Is query syntax using duck typing or not? When I give it a try myself, sure enough this works and appears to prove it's all about duck typing, and not 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;

If duck typing is how query syntax works, as is evidently the case, where might be official (MSDN) documentation about this? Or any reasonable documentation?

Senhauser answered 7/8, 2013 at 21:20 Comment(0)
S
7

There are a few features in C# that the compiler does structural type matching rather than nominal type matching. Examples include the foreach loop, query comprehension syntax (the select, where, etc), and await/async. For all of these features, the compiler is actually just looking for methods with certain names, not specific interfaces or classes.

The reason these features are not tied to specific interfaces is to decouple the language from the .NET framework implementation as much as possible. I suppose this would be considered a form of duck typing.

Eric Lippert explains the feature and reasoning much more thoroughly here.

I have noticed that the MSDN documentation is often wrong or incomplete about these features.

Songwriter answered 7/8, 2013 at 22:2 Comment(1)
Been a while. Eric Lippert's article is still available here: blogs.msdn.microsoft.com/ericlippert/2011/06/30/…Thurgau
N
4

What you're missing is that List<T> implements IEnumerable<T>. Thus, "I always thought this syntax was limited to operating on IEnumerable" is technically true, though in a limited fashion. IQueryable implements IEnumerable as well, along with IList and arrays. Thus, you can perform linq queries against anything that implements IEnumerable.

Since Joker<> doesn't implement IEnumerable<>, your query attempt will fail. The Select<>(), Where<>(), etc. extension methods are built around IEnumerable<>. So, if you want to select from oof, you just need to update your definition of Joker<>

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

(Edit: Answer did make some sense in the context of the originally-formatted question. Edited question makes my response obsolete)

Nickels answered 7/8, 2013 at 21:26 Comment(3)
Edited my post - and the code is working without IEnumerable involved at all. Go figure.Senhauser
Wow, how about that. Maybe the compiler gets super reflection-y at runtime and just converts from a in oof to "Find a Select extension method for the type passed in` and it ends up working? Definitely didn't expect that. Fascinating.Nickels
No (run-time) reflection...just plain old "duck typing." Read the wikipedia article I linked. :)Senhauser

© 2022 - 2024 — McMap. All rights reserved.