https://blog.csdn.net/SerenaHaven/article/details/80047622
委托
委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If … Else(Switch)语句,同时使得程序具有更好的可扩展性。
委托是安全封装方法的类型,类似于 C 和 C++ 中的函数指针。 与 C 函数指针不同的是,委托是面向对象的、类型安全的和可靠的。
为什么说委托是类型安全可靠的?
类型安全要求可以相互转换的不同类型数据在转换时 显式转换
public delegate void GreetingDelegate(string name);
class Program
{
private static void EnglishGreeting(string name)
{
Console.WriteLine("Good Morning, " + name);
}
private static void ChineseGreeting(string name)
{
Console.WriteLine("早上好, " + name);
}
private static void GreetPeople(string name, GreetingDelegate MakeGreeting)
{
MakeGreeting(name);
}
static void Main(string[] args)
{
GreetPeople("Liker", EnglishGreeting);
GreetPeople("李志中", ChineseGreeting);
Console.ReadLine();
}
}
而既然委托 GreetingDelegate 和类型 string 的地位一样,都是定义了一种参数类型,那么我们可以这样使用委托。
static void Main(string[] args)
{
GreetingDelegate delegate1, delegate2;//声明两个委托delegate1,delegate2
delegate1 = EnglishGreeting;//将EnglishGreeting方法绑定到delegate1委托上
delegate2 = ChineseGreeting;
GreetPeople("Liker", delegate1);//使用委托来传递参数
GreetPeople("李志中", delegate2);
Console.ReadLine();
}
委托的一个特性:可以将多个方法赋给同一个委托,或者叫将多个方法绑定到同一个委托,当调用这个委托的时候,将依次调用其所绑定的方法。
也可以直接通过委托来调用方法,此时方法已经被委托封装。
static void Main(string[] args)
{
GreetingDelegate delegate1;//声明委托delegate1
delegate1 = EnglishGreeting;//将EnglishGreeting方法绑定到delegate1委托上
delegate1 += ChineseGreeting;
delegate1("Liker");
Console.ReadLine();
}
注意这里,第一次用的“=”,是赋值的语法;第二次,用的是“+=”,是绑定的语法。如果第一次就使用“+=”,将出现“使用了未赋值的局部变量”的编译错误。同理“-=”意为取消绑定。
也可以这么使用委托,像声明一个对象一样。
GreetingDelegate delegate1 = new GreetingDelegate(EnglishGreeting);//声明delegate1 委托并且将EnglishGreeting方法绑定
delegate1 += ChineseGreeting;//添加绑定
总结:使用委托可以将多个方法绑定到同一个委托变量,当调用此变量时(这里用“调用”这个词,是因为此变量代表一个方法),可以依次调用所有绑定的方法。
匿名函数
常规的委托:
为什么要用匿名方法,什么时候用?
当用常规的委托调用方法时使代码很混乱或者不简洁的时候,可以使用匿名方法(内联代码块定义)。什么意思呢,我们用委托调用方法时,是根据方法名调用的,当需要待用的方法很多时,我们创建很多的方法,但是很多方法又不常用,现在我们可以用匿名方法代替。
匿名方法总是以一个delegate关键字开始
class SomeClass
{
delegate void SomeDelegate();
void SomeMethod()
{
Console.WriteLine("Hello");
}
public void InvokeMethod()
{
SomeDelegate del = new SomeDelegate(SomeMethod);
del();
}
}
可以用一个匿名方法来定义和实现这个方法:
class SomeClass
{
delegate void SomeDelegate();
public void InvokeMethod()
{
SomeDelegate del = delegate(){Console.WriteLine("Hello");};
del();
}
}
lambda表达式(可以把匿名函数进一步简化)
SomeDelegate del =()=>{Console.WriteLine("Hello");};
效果等同于上
闭包
闭包其实就是使用的变量已经脱离其作用域,却由于和作用域存在上下文关系,从而可以在当前环境中继续使用其上文环境中所定义的一种函数对象。
简单地讲,闭包是一个代码块(在C#中,指的是匿名方法或者Lambda表达式,也就是匿名函数),并且这个代码块使用到了代码块以外的变量,于是这个代码块和用到的代码块以外的变量(上下文)被“封闭地包在一起”。当使用此代码块时,该代码块里使用的外部变量的值,是使用该代码块时的值,并不一定是创建该代码块时的值。
从本质上说,闭包是一段可以在晚些时候执行的代码块,但是这段代码块依然维护着它第一个被创建时环境(执行上下文)- 即它仍可以使用创建它的方法中局部变量,即使那个方法已经执行完了。
一句话概括,闭包是一个包含了上下文环境的匿名函数。
public class TCloser
{
public Func<int> T1()
{
var n = 999;
return () =>
{
Console.WriteLine(n);
return n;
};
}
}
class Program
{
static void Main()
{
var a = new TCloser();
var b = a.T1();
Console.WriteLine(b());
}
}
从上面的代码我们不难看到,变量n实际上是属于函数T1的局部变量,它本来生命周期应该是伴随着函数T1的调用结束而被释放掉的,但这里我们却在返回的委托b中仍然能调用它,这里正是闭包所展示出来的威力。因为T1调用返回的匿名委托的代码片段中我们用到了n,而在编译器看来,这些都是合法的,因为返回的委托b和函数T1存在上下文关系,也就是说匿名委托b是允许使用它所在的函数或者类里面的局部变量的,于是编译器通过一系列动作使b中调用的函数T1的局部变量自动闭合,从而使该局部变量满足新的作用范围。
闭包的好处
可以轻松的访问外层函数定义的变量,这在匿名方法中普遍使用