目录
正文
1、匿名方法的由来
没有匿名方法的时候(C#1.0)
addButton.Click+=new EventHandler(AddClick);
void AddClick(object sender, EventArgs e)
{
addButton.Text = "hello";
}
有了匿名方法之后(C#2.0)
addButton.Click += delegate
{
addButton.Text = "hello";
};
2、匿名方法的简介
匿名方法允许我们以一种“内联”的方式来编写方法代码,将代码直接与委托实例相关联,从而使得委托实例化的工作更加直观和方便。
匿名方法的几个相关问题:
-参数列表
-返回值
-外部变量
3、匿名方法的参数
匿名方法可以在delegate关键字后跟一个参数列表(可以不指定,但若指定必须完全匹配),后面的代码则可以访问这些参数。
addButton.Click += delegate(object sender,EventArgs e)
{
MessageBox.Show(((Button)sender).Text);
};
注意“不指定参数列表”与“参数列表为空”的区别
addButton.Click += delegate { }; //正确
addButton.Click += delegate() { }; //错误
4、匿名方法的返回值
如果委托类型的返回值类型为void,匿名方法里便不能返回任何值。
如果委托类型的返回值类型不为void,匿名方法里返回的值必须和委托类型的返回值兼容。
delegate void MyDelegate();
MyDelegate d = delegate
{
return; //也可以不写return
};
delegate int MyDelegate();
MyDelegate d = delegate
{
return 100;
};
5、匿名方法的外部变量
一些局部变量和参数有可能被匿名方法所使用,它们被称为“匿名方法的外部变量”。
外部变量的生存期会由于“匿名方法的捕获效益”而延长――一直延长到委托实例不被引用为止。
delegate double Function(int x);
static void Foo(double factor)
{
Function f = delegate(int x)
{
factor += 0.2;//factor为外部变量
return x * factor;
};
Invoke(f);//factor的生存期被延长
}
void Invoke(Function f)
{
for (int i = 0; i < 5; i++)
{
f(i);
}
}
6、委托类型的推断
C#2.0允许我们在进行委托实例化时,省略掉委托类型,而直接采用方法名,C#编译器会做合理的推断。
C#1.0中的做法
delegate double Function(double a);
addButton.Click += new EventHandler(AddClick);
Apply(a, new Function(Math.Sin));
C#2.0中的做法
delegate double Function(double a);
addButton.Click += AddClick;
Apply(a, Math.Sin);
7、匿名方法的机制
C#2.0中的匿名方法仅仅是通过编译器的一层额外处理,来简化委托实例化的工作。它与C#1.0的代码不存在根本性的差别。
通过ILDasm.exe反汇编工具,我们可以获得对匿名方法的深入了解:
-静态方法中的匿名方法
-实例方法中的匿名方法
-匿名方法中的外部变量
8、静态方法中的匿名方法
public delegate void D();
static void F()
{
D d = delegate { Console.WriteLine("test"); };
}
上面的代码将被编译器转换为:
static void F()
{
D d = new D(__method1);
}
static void __method1()
{
Console.WriteLine("test");
}
9、实例方法中的匿名方法
public delegate void D();
void F()
{
D d = delegate { Console.WriteLine("test"); };
}
上面的代码将被编译器转换为:
void F()
{
D d = new D(__method1);
}
void __method1()
{
Console.WriteLine("test");
}
10、匿名方法中的外部变量
public delegate void D();
void F()
{
int y = 123;
D d = delegate { Console.WriteLine(y); };
}
上面的代码被编译器转换为:
class __Temp
{
public int y;
public int __Method1()
{
Console.WriteLine(y);
}
}
void F()
{
__Temp t = new __Temp();
t.y = 123;
D d = new D(t.__Method1);
}
二、Lambda表达式
1、初识Lambda表达式
使用C#1.0查找“内部包含abc子串的所有字符串”:
delegate bool MyDelegate(string s);
MyDelegate d = new MyDelegate(__Method1);
list.FindAll(d);
bool __Method1(string s)
{
return s.IndexOf("abc") >= 0;
}
使用C#2.0中的匿名方法查找“内部包含abc子串的所有字符串”:
list.FindAll(
delegate(string s)
{
return s.IndexOf("abc") >= 0;
}
);
使用C#3.0中的Lambda表达式查找“内部包含abc子串的所有字符串”:
list.FindAll(s => s.IndexOf("abc") >= 0);
2、Lambda表达式简介
C#2.0的匿名方法允许我们以内联的方式来实现委托实例,而C#3.0的Lambda表达式允许我们使用一种更接近人的思维、更自然的方式来实现类似于匿名方法同样的效果。
3、Lambda表达式格式
Lambda表达式格式为:
(参数列表)=>表达式或者语句块
可以有多个参数、一个参数,或者无参数。参数类型可以隐式或者显示。例如:
(x, y) => x * y; //多参数,隐式类型=>表达式
x => x * 10; //单参数,隐式类型=>表达式
x => { return x * 10; }; //单参数,隐式类型=>语句块
(int x) => x * 10; //单参数,显示类型=>表达式
(int x) => { return x * 10; }; //单参数,显示类型=>语句块
() => Console.WriteLine(); //无参数=>表达式
4、Lambda表达式格式要点
Lambda表达式的参数类型可以忽略,因为可以根据使用的上下文进行推断。
Lambda表达式的主体(body)可以是表达式,也可以是语句块。
Lambda表达式传入的实参将参与类型推断,以及方法重载辨析。
Lambda表达式表达式和表达式体可以被转换为表达式树。
5、Lambda表达式与委托类型
Lambda表达式L可以被转换为委托类型D,需要满足以下条件:
L和D具有相同的参数个数。
L的参数类型要与D的参数类型相同。注意隐式类型要参与类型辨析。
D的返回值类型与L相同,无论L是表达式,还是语句块。
三、补充
1、匿名方法和Lambda表达式都表示一个委托类型的对象。
匿名方法和Lambda表达式都表示一个委托类型的对象。或者说是可以转换为委托类型的对象。
参看下例:
public class MyClass
{
delegate int MyDelegate(int x,int y);
static void Process(MyDelegate md) { }
static void test()
{
//第一种调用方式(C#1.0)
MyDelegate md = new MyDelegate(__Method);
MyClass.Process(md);
//第二种调用方式(C#2.0)匿名方法
MyClass.Process(delegate(int x, int y) { return x * y; });
//第三种调用方式(C#3.0)Lambda表达式
MyClass.Process((x, y) => { return x * y; });
}
static int __Method(int x,int y)
{
return x * y;
}}