Full Lectures Set
- C#Lectures - Lecture 1: Primitive Types
- C# Lectures - Lecture 2: Work with text in C#: char, string, StringBuilder, SecureString
- C# Lectures - Lecture 3 Designing Types in C#. Basics You Need to Know About Classes
- C# Lectures - Lecture 4: OOP basics: Abstraction, Encapsulation, Inheritance, Polymorphism by C# example
- C# Lectures - Lecture 5:Events, Delegates, Delegates Chain by C# example
- C# Lectures - Lecture 6: Attributes, Custom attributes in C#
- C# Lectures - Lecture 7: Reflection by C# example
- C# Lectures - Lecture 8: Disaster recovery. Exceptions and error handling by C# example
- C# Lectures - Lecture 9:Lambda expressions
- C# Lectures - Lecture 10: LINQ introduction, LINQ to 0bjects Part 1
Introduction
In this article I'm going to talk about anonymous functions that are named lambda expressions. Lambda expressions is the relatively new technology that allows you create delegates or expression tree types. Using lambda expressions you can create functions that you pass as arguments to another functions or return them as value of function calls. Also lambda expressions are more than useful with LINQ. Most developers and writers describe lambda expressions together with delegates and events, but as this is relatively new technology, I've decided to have separate article about it. Since version 3, C# has supported lambda expressions. Lambda expressions have been used in computer languages as far back as LISP; they were conceptualized in 1936 by Alonzo Church, an American mathematician. These expressions provide shorthand syntax for specifying an algorithm.
Anonymous types
Before diving into lambda expressions I want to highlight quickly another short topic - anonymous types. Anonymous types provide a convenient way to encapsulate read-only properties to single object without definition of type for this object. While compilation compiler generates a name for the anonymous type, but it is not visible for developer. Type for the read-only properties in anonymous type is also defined by compiler. By itself anonymous types doesn't seems to be something cool, but in combination with LINQ and Lambda expressions they give a real power. We'll review it later on in this chapter.
Following code demonstrates usage of anonymous type:
Console.WriteLine("-----------ANONYMOUS TYPES----------------");
var anonym1 = new { int_val = 1, double_val = 0.11, string_val = "some string" };
Console.WriteLine("anonym1 type is: " + anonym1.GetType());
Console.WriteLine("double_val type is: " + anonym1.double_val.GetType());
Console.WriteLine("int_val type is: " + anonym1.int_val.GetType());
Console.WriteLine("string_val type is: " + anonym1.string_val.GetType());
Result of it will be:
Anonymous types limitations:
- They can have only public read-only properties
- They don't have methods or other type members
- You can cast anonymous type only to object
- If two anonymous types have equal definition they are treated by compiler as same type and share same metadata
There are several ways of initialization for anonymous types. Few of them are provided below:
int i = 10;
string s = "string value";
var anonym2 = new { int_val = i, string_val = s };
var anonym3 = new { i, s };
var anonym4 = new { anonym2, anonym3 };
Building lambda expression, lambda operator
When you create lambda expression you should use lambda operator =>. Lambda operator separates input variable from the left side with the lambda body on the right. Lambda operator can be read as "goes to". In lambda expression you put input parameters, if you have them, on the left side of operator and expression or statement from the right side of the lambda operator. So lambda expression looks like:
<arguments if any> => <expression>
or
<arguments if any> => <statement block>
Initially before lambda expressions there were designed anonymous methods that you can build path them to functions that expect delegates. Sometimes this is very convenient and useful\readable in code. Lambda then was created to provide more specific syntax for anonymous methods as delegates and for sure they are more than useful for LINQ query expressions as they provide with ability to write compact methods that you path as arguments for subsequent evaluation.
Below is an example how we replace delegate with lambda function:
public delegate int MyDelegate(string s, int i);
public static void DelegateCaller(MyDelegate input)
{
int res = input("Delegate Caller", 5);
Console.WriteLine("Delegate result is: " + res);
}
public static int DelegateFunction(string s, int i)
{
Console.WriteLine("Delegate function string: " + s + " int:" + i);
return 1;
}
Console.WriteLine("-----------LAMBDA EXPRESSIONS----------------");
DelegateCaller(new MyDelegate(DelegateFunction));
MyDelegate del = (str, dig) =>
{
Console.WriteLine("Delegate lambda string: " + str + " int: " + dig);
return 2;
};
DelegateCaller(new MyDelegate(del));
To play with the example you can download sources attached to this article
Lambda expression syntaxes
- You can specify parameters in parenthesis is you need to path more than one parameter:
(car, engineSize) => car.engine >= engine Size
- You can specify types for parameters to avoid confusion
<code> (ClassCar car, int engineSize) => car.engine >= engine Size</code>
- You can have lambda expression without parameters:
<code> () => Console.WriteLine("Parameter less lambda");</code>
- If you have multiple statements in body they should be in curly braces:
x =>
{
Console.WriteLine("Few statements lambda");
return x*x;
}
- You can use local variable in expression body that will be visible only inside your lambda expression
x =></code>
Variable scope in lambdas
In lambdas you can refer to variables that are in the scope of method where they are defined and in type where they are defined. If your will use such variable in scope of lambda it will be stored in memory for lambda even if in other cases it would be garbage collected. Please see example below that demonstrates how local variable in method where lambda is defined is still in memory with proper value even so in usual cases it will not work:
public delegate int SomeDelegate(int input);
public void ScopeExample()
{
int j = 5;
m_del = (x) =>
{
Console.WriteLine("Input parameter is : " + x);
Console.WriteLine("Local variable is: " + j);
return j + x;
};
int i = m_del(5);
Console.WriteLine("Delegate internal result is: " + i);
}
Program test = new Program();
test.ScopeExample();
int k = test.m_del(10);
Console.WriteLine("Delegate external result is: " + k);
You can download sources attached to play with it
I'm copying below from MSDN rules for variable scope in lambdas:
- A variable that is captured will not be garbage-collected until the delegate that references it goes out of scope.
- Variables introduced within a lambda expression are not visible in the outer method.
- A lambda expression cannot directly capture a ref or out parameter from an enclosing method.
- A return statement in a lambda expression does not cause the enclosing method to return.
- A lambda expression cannot contain a goto statement, break statement, or continue statement whose target is outside the body or in the body of a contained anonymous function.
Sources