深入理解C#(十六)

*第九章(C# 3:Lambda表达式和表达树)

lambda=λ

作为委托的lambda表达式

Func<…>委托类型简介

.NET3.5的System命名空间中,有5个泛型Func委托类型。
每个委托签名都获取0~4个参数,其类型使用类型参数来指定。最后一个类型参数用作每种情况下的返回类型。

例如:Func<string,double,int>等价于public delegate int SomeDelegate(string arg1,double arg2)

转换成lambda表达式

用匿名方法来创建委托实例

1
2
3
Func<string,int> returnLength;
returnLength=delegate(string text){return text.Length;};
Console.WriteLine(returnLength("Hello"));

Lambda表达式最冗长的形式是:

(显示类型的参数列表)=》{语句}
例子:

1
2
3
4
Func<string, int> returnLength;
returnLength = (string text) => { return text.Length; };

Console.WriteLine(returnLength("Hello"));

Lambda表达式有着和匿名方法控制返回语句一样的规则:不能从Lambda表达式返回void类型

使用单一表达式作为主体

大多数情况可以用一个表达式来表示整个主体,该表达式的值是Lambda的结果

1
(string text)=>text.Length

隐式类型的参数列表

编译器能推断出参数类型,可以不用显示声明参数类型,注意:隐式和显示类型参数不能一起用。此外要是有out或ref参数,只能使用显示参数。

1
(text)=>text.Length

单一参数的快捷语法

如果lambda表达式只需要一个参数,且那个参数可以隐式指定类型,可以省略圆括号

1
text=>text.Length

现在,例子简化成:

1
2
3
4
 Func<string, int> returnLength;
returnLength = text => text.Length;

Console.WriteLine(returnLength("Hello"));

高阶函数

Lambda表达式主体可以包含另一个Lambda表达式,另外,Lambda表达式的参数可以是另一个委托

使用List和事件的简单例子

列表的过滤、排序和操作

List的FindAll方法,获取一个Predicate,并返回一个新列表,包含原始列表中与谓词匹配的所有元素。Sort方法获取一个Comparison,并相应地对列表进行排序。ForEach方法获取一个Action,并对每个元素执行特定行为。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
var films = new List<Film>
{
new Film {Name="Jaws", Year=1975},
new Film {Name="Singing in the Rain", Year=1952},
new Film {Name="Some like it Hot", Year=1959},
new Film {Name="The Wizard of Oz", Year=1939},
new Film {Name="It's a Wonderful Life", Year=1946},
new Film {Name="American Beauty", Year=1999},
new Film {Name="High Fidelity", Year=2000},
new Film {Name="The Usual Suspects", Year=1995}
};

Action<Film> print =
film => Console.WriteLine("Name={0}, Year={1}", film.Name, film.Year);

// Note: extra lines added for clarity when running
Console.WriteLine("All films");
films.ForEach(print);
Console.WriteLine();

Console.WriteLine("Oldies");
films.FindAll(film => film.Year < 1960)
.ForEach(print);
Console.WriteLine();

Console.WriteLine("Sorted");
films.Sort((f1, f2) => f1.Name.CompareTo(f2.Name));
films.ForEach(print);

Console.Read();

创建委托实例,用来打印列表中的项,因为之后会使用三次,所以用变量print来保存,而不用每次都单独使用一个Lambda表达式。它可以打印一个film类型,通过foreach就能打印整个列表。
Action<Film> print = film => Console.WriteLine("Name={0}, Year={1}", film.Name, film.Year);

1
`films.ForEach(print);`

在事件处理程序中进行记录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
static void Log(string title, object sender, EventArgs e)
{
Console.WriteLine("Event: {0}", title);
Console.WriteLine(" Sender: {0}", sender);
Console.WriteLine(" Arguments: {0}", e.GetType());
foreach (PropertyDescriptor prop in
TypeDescriptor.GetProperties(e))
{
string name = prop.DisplayName;
object value = prop.GetValue(e);
Console.WriteLine(" {0}={1}", name, value);
}
}

static void Main()
{
Button button = new Button();
button.Text = "Click me";
button.Click += (src, e) => Log("Click", src, e);
button.KeyPress += (src, e) => Log("KeyPress", src, e);
button.MouseClick += (src, e) => Log("MouseClick", src, e);

Form form = new Form();
form.AutoSize = true;
form.Controls.Add(button);
Application.Run(form);
}

表达式树

以编程方式构建表达式树,编译并执行

1
2
3
4
5
6
7
Expression firstArg = Expression.Constant(2);
Expression secondArg = Expression.Constant(3);
Expression add = Expression.Add(firstArg, secondArg);

Func<int> compiled = Expression.Lambda<Func<int>>(add).Compile();
Console.WriteLine(compiled());
Console.Read();

将C# Lambda表达式转换成表达式树

1
2
3
Expression<Func<int>> return5 = () => 5;
Func<int> compiled = return5.Compile();
Console.WriteLine(compiled());

()=》 5是Lambda表达式