【笔记】【LINQ编程技术内幕】第五章 理解Lambda表达式和闭包

了解由函数指针到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
引用类型得闭包,会修改原局部变量得值

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhy29563

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值