tags:

views:

536

answers:

5

I am trying to understand LINQ and become confident at using it. What I am struggling with are the parameters asked for. Example:

var sortedWords = words.OrderBy(a=>a.Length)

words is an array collection. OrderBy's intellisense says:

Func<string, TKey> keyselector

A func executes a method, and a string is the value, TKey a key.

In the example http://msdn.microsoft.com/en-us/vcsharp/aa336756.aspx#thenBySimple (ThenBy - Comparer), we compare length by saying a => a.Length. I understand that syntax, but how is that related to what the intellisense is asking for?

I tend to find the method signature and intellisense unreadable because of all the generics.

Thanks.

A: 

I never really worry about the Intellisense, to be honest. It was messing me up early in my Linqage. As I spent more time with generics and expressions, it started to make sense, but up until then I just pumped in the syntax.

What it wants is a lambda expression that tells Linq what to look for in order to sort your collection.

I feel you, my brother, hang in there and it will make sense very soon.

KevDog
+6  A: 

The type (as displayed by Intellisense) makes sense if you understand the nature of lambda expressions in .NET/C#. Otherwise, it can indeed seem a bit strange to the newcomer. Begin by considering that the type of keySelector, Func<TSource, TKey> is simply a delegate. Before C# 3.0, you would call such a method by passing a delegate as a parameter, for example:

IEnumerable<string> sortedWords = words.OrderBy(new Func<string, int>(mySelectorMethod));

where mySelectorMethod is the name of an ordinary method which takes a string as a parameter and returns an int. (As a side point, I suppose you could use anonymous delegates, but let's not go there for now.) Also, note that this example is purely illustrative, as LINQ is almost always used with .NET 3.5/C# 3.0 (though I believe it can be used with either/both .NET 2.0/C# 2.0 - someone correct me if I'm wrong). Since C# 3.0, methods can be defined inline as lambda expressions, which are intended to be used in precisely such circumstances as these. Read over the MSDN article on lambda expressions (linked above) if you want to get a proper introduction, but here I will simply describe the use in this specific context. As you state, your code (in C# 3.0) is something like the following:

var sortedWords = words.OrderBy(a => a.Length);

The part of the expression that is a => a.Length is the lambda expression, which is really just shorthand for declaring a function inline. The syntax of lambda expressions is quite simple for the most part; to the left of the => the arguments are specified, generally in the form (arg1, arg2, arg3), but since there's only one in this case you can omit the brackets. To the right of the => is the expression that is the return value of the function (lambda expression more accurately). Alternatively you can enclose actual code with a return statement within { and } though this is usually unnecessary. What I believe the C# compiler does is recognises the parameter passed to OrderBy as a lambda expression and then compiles it into a function and creates and passes the delegate for you. Note that lambda expressions can also be converted to System.Linq.Expressions.Expression objects (accessible expression trees) instead of delegates, but this is a much less common usage. Anyway, there's a lot going on behind the scenes here, but hopefully this should at least clarify why the type is Func<TSource, TKey> and how it relates to the lambda expression. As I said, read up on MSDN if you want a deeper understanding of LINQ/lambdas/delegates...

Noldorin
+1  A: 

Hi, I think the IntelliSense is actually quite useful, especially for generic methods that take Func<..> type as an argument, because you can see the types and types guide you to understand what the method might do.

For example, the arguments for OrderBy are IEnumerable<string> as a 'this' argument, which means that we have some input containing collection of strings. The first argument keySelector has a type Func<string, TKey>, which means that it is some lambda expression you provide that specifies how to get TKey from string.

This already suggests that the method will probably enumerate over all the items (strings) in the collection and it can use the keySelector to get value of type TKey from every element in the collection. The name TKey already suggests that it will use this value to compare the elements (strings) using this calculated key. However, if you look at the other overload that takes IComparer<TKey> then you can be sure about this - this argument specifies more details about how do you want to compare two values of type TKey, so the function must compare the elements using this key.

... this kind of thinking about types takes some time to get used to, but once you'll learn it, it can be extremely helpful. It is more useful in "functional" style of code, which often uses a lot of generics and lamdba expressions in C# 3.0 (and similar things in functional languages like F# or others)

Tomas Petricek
To make myself clearer. it's really the presentation and spacing of the intellisense sentence rather than anything else which makes it a little hard on the eye. :)
dotnetdev
Yeah, I agree it could be a bit clearer. I think it's a bit difficult with the C# syntax. You can take a look at F#, which (I think) displays similar information in much concise way...
Tomas Petricek
A: 

OrderBy() takes a delegate for a function that accepts a single parameter (in your case, a string) and returns a value of the type that is substituted for TKey. It may be that the parameter type (string) was already determined since you called the method on an IEnumerable<string> but the delegate type will only be resolved as Func<string, int> after it infers it from the lambda expression when it is fully specified (i.e., a => a.Length). If you haven't given the parser any clues as to what you want as a sort key, it'll just show TKey in IntelliSense until it can determine the intended type.

Mark Cidade
+5  A: 
a => a.Length

I understand that syntax, but how is that related to what the intellisense is asking for?

This chunk of code is a lambda expression. A lambda expression is a handy way of generating an Anonymous method (in this case), or a System.Linq.Expressions.Expression . Let's break it down by parts.

  • The most noticeable feature is the =>, which seperates parameters from a method body.
  • On the left side of the =>, there is a symbol: a. This is the declaration of a parameter for our anonymous method. The compiler is aware that we are calling OrderBy(), and that OrderBy requires a Func<string, object>. The parameter for such a function is a string, so the compiler determines that a must be a string. The only thing the programmer needed to provide is a name.
  • On the right side of the =>, there is method body. Since this is a one-liner, the return keyword is implied. The IDE provides intellisense against a as a string, which allows you to use the Length property.

Now, consider this C# 2.0 ...

IEnumerable<string> sortedWords = 
  Enumerable.OrderBy(words, delegate(string a) {return a.Length;});

With the C# 3.0

IEnumerable<string> sortedWords = words
  .OrderBy(a => a.Length);
David B
Thanks for this. Makes perfect sense now! :)
dotnetdev
Also, if I was to get the shortest string first, is there a way other than saying a => a.Length and then Reverse on the collection?
dotnetdev
If you want to change sort orders, check out OrderBy, OrderByDescending, ThenBy and ThenByDescending.
David B