TechEd 2008 notes: Understanding C# Lambda Expressions
This was a lunch session, so pretty short. It turned out to be pretty introductory-level, just about grokking the syntax (which I pretty much already did).
Understanding C# Lambda Expressions
Scott Cate
myKB.com
Operators: most are very mnemonic, very self-explanatory. But what about =>?
Read => as “reads into” or “feeds into”.
BuildMatrix((a, b) => (a+b).ToString());
History of Delegates in .NET
- Forget the compiler for a minute. What does the word “delegate” mean? “I don’t have time to (or don’t know how to) do this; I’m going to offload it to someone else and take the credit.”
- You’ve mastered delegates if you could write a class with an event, and wire up that event, in Notepad, without Intellisense.
- Define the delegate type. No implementation, just a signature.
- .NET 1.0: Pass the method name as a parameter (or whatever). (Actually, in 1.0 I think maybe you had to do
new DelegateType(MethodName).) - .NET 2.0: anonymous delegates.
BuildMatrix(delegate(int a, int b) { return (a+b).ToString(); } - Return type is inferred.
- .NET 3.0:
BuildMatrix((a, b) => (a+b).ToString()); - The stuff after
=>is an anonymous delegate. The stuff before it is the parameter list. - You can define the parameter types, or the compiler can infer them.
- Can omit parentheses if there’s only one parameter.
- Same thing without predefining the delegate type:
void BuildMatrix(Func<int, int, string> operate) - Don’t need curly braces and don’t need
return— can just put an expression. You can also put braces with statements (in which case you do needreturn).
Errata — details the presenter got mixed up:
- Built a grid in HTML, and it looks like he got his X and Y loops reversed. Yes, this is nitpicky.
- Told us you can make two delegates with the same name but different parameters (one with two ints and one with two decimals). You can’t; it won’t compile (I double-checked). They can have the same name if they have a different number of generic parameters (since the number of generic parameters is part of the name), but you can’t do it if they’re non-generic.
- Told us the compiler generates a new delegate type behind the scenes when you pass an anonymous delegate (it doesn’t, it uses the actual delegate type that the function expects; I double-checked)
June 10th, 2008 at 9:03 am
Joe,
Wow – this is great that you took amazing notes. I really like what you’ve shared.
About the errata – this is great news as well. Here is my response to all 3 items.
1) I’m confused about the x,y mix up you mention, but I’m curious to solve it. here is the code sample. Maybe you’re saying I mixed up the X and Y in my verbal delivery?
Table table = new Table(); for( int x = 0; x <= 10; x++ ) { TableRow row = new TableRow(); for( int y = 0; y <= 10; y++ ) { TableCell cell = new TableCell(); cell.BorderWidth = new Unit( 1 ); cell.BorderColor = Color.Black; string val = operate( x, y ); LiteralControl lit = new LiteralControl( val ); cell.Controls.Add( lit ); row.Cells.Add( cell ); } table.Rows.Add( row ); } MatrixPlaceHolder.Controls.Clear(); MatrixPlaceHolder.Controls.Add( table );As far as the two delegates with the same name, but diff arg types, here is what I was trying to say, but may have fumbled.
public delegate string DoMath(int a, int b); public delegate string DoMath(decimal a, decimal b);Won’t compile. To get this usage, you need to drop to generics, with the following delegate signature.
public delegate string DoMath<X, Y>(X a, Y b);Which is the same as what’s built into the 3.5 System.Core
public delegate Z Func<X, Y, Z>(X a, Y b);On point 3, this only works if you have a matching delegate defined. In 3.5 System.Core, there are several (mentioned above) Func that are pre built, that pretty much handle everything for you. However, it only supports up to 4 parameters, and 1 return type. Anything over that needs a custom delegate Func built. You are correct that the compiler doesn’t build this for you automatically, and I’ll change the way this is described in the future.
Thank you for the awesome notes!
June 10th, 2008 at 9:06 am
Excellent blog post. Good job pointing out the errata as well.
June 11th, 2008 at 6:42 am
Scott:
You’re using a variable called
xto hold your Y (row) index, andyfor your X (column) index. Typicallyywould be the outer loop, andxthe inner, especially when you’re doing something like HTML where you’re forced to generate rows first and cells inside them.It works, as your demos showed, but your naming flies in the face of convention, and will confuse the heck out of the next person to maintain this code. Just change the
for(x)line tofor(y), and vice versa.Thanks for the clarification on the other items.
June 12th, 2008 at 11:56 pm
I would like to follow up on the X and Y variable names, and possible get some other input. When I’m nesting a loop, I always increment the variable names. So I would use A on the outside, and B in the inside.
In this case I was thinking X-Axis (horizontal for rows) and Y-Axis (vertical for columns), which also matches my increasing variable names.
“Typically y would be the outer loop, and x the inner”
Can you help me understand why/where this came from?
Thanks again for your notes, and helping me get to the bottom of this.
June 13th, 2008 at 6:10 am
Like I already said, Y is traditionally the vertical coordinate (how far down the page you are), and X is the horizontal coordinate (how far across the page you are). It’s that way in every mathematics textbook I’ve ever seen, and every computer-graphics coordinate system I’ve ever seen. That’s how you do coordinates.
You got them backwards, plain and simple. “Which column” is not Y, because the column number has nothing to do with how far down the page you are — it’s how far across the page you are. That makes it the X coordinate in the (X, Y) pair.
I’m surprised you’ve never seen this pattern before. I’ve seen it dozens of times, in my college coursework, in books, and in examples online. This is how you do a two-dimensional grid, in a world where text flows horizontally — you do
yas the outer loop,xas the inner loop, so thatxis your X coordinate andyis your Y coordinate.If you’re going to use arbitrary incrementing variable names, that’s no problem. You used
aandbfor your delegate parameter names, which I thought was perfect (and in keeping with convention, as well — there are many examples out there ofAddandSubtractandMultiplythat take parameters namedaandb). But if you’re going to use arbitrary variable names, make sure they are arbitrary — don’t use ones that already have a different meaning for your problem domain. Useaandb, like you did for your delegate parameters; oriandj. But don’t usexandyfor your Y and X coordinates.