了解由函数指针到Lambda表达式得演化过程
C++函数指针 ,其实这个例子使用的是C++\CLI
#include "stdafx.h"
using namespace System;
typedef void (*FunctionPointer)(System::String ^str);
void HelloWorld(System::String ^str)
{
Console::WriteLine(str);
Console::ReadLine();
}
int main(array<System::String ^> ^args)
{
FunctionPointer fp = HelloWorld;
fp(L"Hello World");
return 0;
}
C# 函数指针
delegate void FunctionPointer(String str);
static void Main(string[] args)
{
FunctionPointer fp = HelloWorld;
fp("Hello World!");
}
static void HelloWorld(string str)
{
Console.WriteLine(str);
Console.ReadLine();
}
匿名委托
delegate void FunctionPointer(string str);
static void Main(string[] args)
{
FunctionPointer fp = delegate (string s)
{
Console.WriteLine(s);
Console.ReadLine();
};
fp("Hello World!");
}
Lambda表达式
delegate void FunctionPointer(string str);
static void Main(string[] args)
{
FunctionPointer fp = s => Console.WriteLine(s);
fp("Hello World!");
Console.ReadLine();
}
使用.NET 框架自带得泛型委托
Action<double> print = amount => Console.WriteLine("{0:c}", amount);
Action<double> michiganSalesTax = amount => print(amount *= 1.06);
var amounts = new double[]{10.36, 12.00, 134};
Array.ForEach<double>(amounts, michiganSalesTax);
编写基本的Lambda表达式
System命名空间中定义了泛型委托Action、Func以及Predicate,Lambda表达式可以赋值给这些类型的实例或匿名类型。
- Action 用于在泛型参数上执行一个操作
- Func 用于在参数上执行一个操作,并返回一个值
- Predicate 用于定义一组条件却确定参数是否复核这些条件
自动属性
class Program
{
static void Main(string[] args)
{
IronChef Batali = new IronChef
{
Name = "Mariio Batali",
Specialty = "Italian"
};
Console.WriteLine(Batali.Name);
}
}
public class IronChef
{
public string Name { get; set; }
public string Specialty { get; set; }
}
阅读Lambda表达式
Lambda表达式由一个左部、=>、以及一个右部组成,如(x,y) => x + y
,左部表示输入参数,又不表示需要求职的表达式。
Func<int, int, int> add = (int x, int y) => x + y;
Console.WriteLine(add(3, 4));
Lambda表达式用作泛型活动
Action<string, TextWriter> print = (s, writer) => writer.WriteLine(s);
print("Console", Console.Out);
StreamWriter stream = File.AppendText("c:\\temp\\text.txt");
stream.AutoFlush = true;
print("File", stream);
搜索字符串
var recipes = new[]{
"Crepes Florentine",
"Shrimp Che Paul",
"Beef Burgundy",
"Rack of Lamb",
"Tacos",
"Rosemary Raosted Chicken",
"Juevos Rancheros",
"Buffalo Chicken Nachos"
};
var results = recipes.Where( s => s.Contains("Chicken"));
Array.ForEach<string>(results.ToArray<string>(),
s => Console.WriteLine(s));
获取斐波那契数列中的奇数
List<int> fiboList = new List<int>();
fiboList.AddRange(new[] { 1, 1, 2, 3, 5, 8, 13, 21, 34, 55 });
Predicate<int> match = n => n % 2 == 1;
var odds = fiboList.FindAll(match);
ObjectDumper.Write(odds);
Lambda表达式用作泛型谓词
泛型委托Predicate接受一个由T指定的参数并返回一个布尔值。Predicate用在诸如Array.Find和Array.FindAll这样的函数中。Predicate可以通过普通函数、匿名函数或Lambda表达式来初始化。
Rectangle[] rects = new Rectangle[100];
private void Form1_Load(object sender, EventArgs e)
{
Random random = new Random(DateTime.Now.Millisecond);
int x, y, width, height;
for (int i = 0; i < 100; i++)
{
x = random.Next(this.ClientRectangle.Width / 2);
y = random.Next(this.ClientRectangle.Height / 2);
width = random.Next(200);
height = random.Next(200);
rects[i] = new Rectangle(x, y, width, height);
}
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
Random random = new Random(DateTime.Now.Millisecond);
Predicate<Rectangle> area = rect => (rect.Width * rect.Height) <= (random.Next(200) * random.Next(200));
Rectangle[] matches = Array.FindAll(rects, area);
...
}
将Lambda表达式绑定到控件事件
public Form1()
{
InitializeComponent();
button1.Click += (s, e) => MessageBox.Show("Click!");
}
利用Lambda表达式进行动态编程
LINQ时间上是基于扩展方法和Lambda表达式的语法糖
使用Select和Lambda表达式
var numbers = new int[] { 1, 2, 3, 4, 5, 6 };
foreach (var result in numbers.Select(n => n))
Console.WriteLine(result);
利用复合类型初始化投影出一个新类型
var numbers = new int[] { 1, 2, 3, 4, 5, 6 };
foreach (var result in numbers.Select(n => new { Number = n, IsEven = n % 2 == 0 }))
Console.WriteLine(result);
使用Where和Lambda表达式
var words = new string[] { "Drop", "Dead", "Fred" };
IEnumerable<string> hasDAndE = words.Where(s => s.Contains('D') && s.Contains('e'));
foreach (string s in hasDAndE)
Console.WriteLine(s);
使用OrderBy和Lambda表达式
var numbers = new int[]{1, 3, 5, 7, 9, 2, 4, 6, 8, 10 };
IEnumerable<int> ordered = numbers.OrderBy(n => n);
foreach (var num in ordered)
Console.WriteLine(num);
将Lambda表达式编译为代码或数据
Lambda表达式既可以编译为代码,也可以编译为数据。当Lambda表达式赋值给一个变量、字段或委托时,编译器将发出可执行IL。比如,num => num % 2 == 0
所发出的IL跟接收一个整型值,执行除法,然后将余数跟0做比较
的函数所发出的IL是一样的。
当Lambda表达式被赋值给一个类型为System.Linq.Expressions.Expression<TDelegate>
的变量、字段或参数时,编译器发出的代码将时一个表达式树
。
表达式树是Lambda表达式在内存中的表示。这些表达式是能够被编译的,而且他们所基于的Lambda表达式跟别的Lambda表达式一样都能够被调用。不过,更重要的是,Lambda表达式是可以被传输的,而且能够通过引用程序编程接口转换成新的形式。
static void Main(string[] args)
{
Test();
Test2();
}
static void Test2()
{
Expression<Func<int, bool>> exp = num => num % 2 == 0;
Console.WriteLine("Body: {0}", exp.Body.ToString());
Console.WriteLine("Node Type: {0}", exp.NodeType);
Console.WriteLine("Type: {0}", exp.Type);
Func<int, bool> lambda = exp.Compile();
Console.WriteLine(lambda(2));
}
static void Test()
{
Func<int, bool> lambda = num => num % 2 == 0;
}
Lambda表达式和闭包
如果将一个变量声明在一个函数内部,该变量就只会存在于声明范围内的栈内存空间中。因此,在函数返回之后,这个本地变量也就同时被从该函数的栈空间中清除掉了。也就是说,当你在一个Lambda表达式中使用了本地变量的话,该本地变量将会在函数的栈空间清理的时候被移除。为了防止这样的问题发生,当一个依赖于本地变量的Lambda表达式需要从函数中返回出去,编译器就会创建一个闭包。
闭包是一个自动生成的类,它含有一个Lambda表达式,并为每个被调用到的本地变量生成一个字段。这些本地变量的值会被赋值到相应的字段中,一边拓展这些本地变量的生命周期。
static void Main(string[] args)
{
UsesClosure();
}
static void UsesClosure()
{
string toFind = "ed";
var words = new string[]{"ended", "friend", "closed", "potato"};
// Lambda表达式会被编译为一个函数。如果没有闭包,在生成的函数中是
// 无法直接使用局部变量 toFind
// 闭包会创建一个新的类,该类包含一个与局部变量 toFind包含相同值得字段
// 以延续局部变量得生命周期
var matches = words.Select(s => s.Contains(toFind));
foreach (var str in matches)
Console.WriteLine(str);
}
问题:
如果在闭包中改变局部变量得值,原局部变量得值是否改变
一、值类型传递
static void Main(string[] args)
{
test();
}
static void test()
{
var a = 1;
var num = new int[] { 1, 2, 3, 4, 5 };
var num2 = num.Select(n => new {
A = a++,
Num = n + a
});
foreach (var item in num2)
Console.WriteLine("{0}, {1}", item.A, item.Num);
Console.WriteLine(a);
}
结果
1, 3
2, 5
3, 7
4, 9
5, 11
6
值类型得闭包,原局部变量得值会被更改
一、引用类型传递
static void Main(string[] args)
{
UsesClosure();
}
static void UsesClosure()
{
string toFind = "ed";
var words = new string[]{"endeded", "friended", "closeded", "potatoed"};
var matches = words.Select(s => new
{
Local = toFind += "ed",
Contained=s.Contains(toFind)
});
foreach (var item in matches)
Console.WriteLine("{0}, {1}", item.Local, item.Contained);
Console.WriteLine("=================================");
Console.WriteLine(toFind);
}
结果
eded, True
ededed, False
edededed, False
ededededed, False
=================================
ededededed
引用类型得闭包,会修改原局部变量得值