一、委托
1.概念:
委托是函数(方法)的容器 ,可以理解为表示函数(方法)的变量类型。用来存储、传递函数(方法)
委托的本质是一个类,用来定义函数(方法)的类型(返回值和参数的类型),不同的函数(方法)必须对应和各自"格式"一致的委托
2.语法:
关键字 : delegate
语法:访问修饰符 delegate 返回值 委托名(参数列表);
可以申明在 namespace 和 class 语句块中,更多的写在 namespace 中
简单记忆委托语法,就是函数申明语法前面加一个 delegate 关键字
3.自定义委托
// 访问修饰默认不写 为public 在别的命名空间中也能使用
// private 其它命名空间就不能用了
// 一般使用public
// 申明了一个可以用来存储无参无返回值函数的容器
// 这里只是定义了规则 并没有使用
internal delegate void MyFun();
// 委托规则的申明 是不能重名(同一语句块中)
// 表示用来装载或传递 返回值为int 有一个int参数的函数的 委托 容器规则
public delegate int MyFun2(int a);
// 委托是支持 泛型的 可以让返回值和参数 可变 更方便我们的使用
internal delegate T MyFun3<T, K>(T v, K k);
4.使用委托
// 使用定义好的委托
// 委托变量是函数的容器
// 委托常用在:
// 1.作为类的成员
// 2.作为函数的参数
internal class Test
{
public Action action;
public MyFun fun;
public MyFun2 fun2;
public void TestFun(MyFun fun, MyFun2 fun2) {
// 先处理一些别的逻辑 当这些逻辑处理完了 再执行传入的函数
int i = 1;
i *= 2;
i += 2;
// fun();
// fun2(i);
// this.fun = fun;
// this.fun2 = fun2;
}
// 增
public void AddFun(MyFun fun, MyFun2 fun2) {
this.fun += fun;
this.fun2 += fun2;
}
// 删
public void RemoveFun(MyFun fun, MyFun2 fun2) {
//this.fun = this.fun - fun;
this.fun -= fun;
this.fun2 -= fun2;
}
}
// 委托变量可以存储多个函数(多播委托)
internal class Program
{
private static void Main(string[] args) {
Console.WriteLine("委托");
// 专门用来装载 函数的 容器
MyFun f = Fun;
Console.WriteLine("1");
Console.WriteLine("2");
Console.WriteLine("3");
Console.WriteLine("4");
Console.WriteLine("5");
f.Invoke();
MyFun f2 = Fun;
Console.WriteLine("1");
Console.WriteLine("2");
Console.WriteLine("3");
Console.WriteLine("4");
Console.WriteLine("5");
f2();
MyFun2 f3 = Fun2;
Console.WriteLine(f3(1));
MyFun2 f4 = Fun2;
Console.WriteLine(f4.Invoke(3));
Test t = new Test();
t.TestFun(Fun, Fun2);
Console.WriteLine("***************");
// 如何用委托存储多个函数
MyFun ff = null;
// ff = ff + Fun;
ff += Fun;
ff += Fun3;
ff();
// 从容器中移除指定的函数
ff -= Fun;
// 多减 不会报错 无非就是不处理而已
ff -= Fun;
ff();
// 清空容器
ff = null;
if (ff != null) ff();
// 系统定义好的委托
// 使用系统自带委托 需要引用using System;
// 无参无返回值
Action action = Fun;
action += Fun3;
action();
// 可以指定返回值类型的 泛型委托
Func<string> funcString = Fun4;
Func<int> funcInt = Fun5;
// 可以传n个参数的 系统提供了 1到16个参数的委托 直接用就行了
Action<int, string> action2 = Fun6;
// 可以穿n个参数的 并且有返回值的 系统也提供了 16个委托
Func<int, int> func2 = Fun2;
}
private static void Fun() {
Console.WriteLine("张三做什么");
}
private static void Fun3() {
Console.WriteLine("李四做什么");
}
private static string Fun4() {
return "";
}
private static int Fun5() {
return 1;
}
private static void Fun6(int i, string s) { }
private static int Fun2(int value) {
return value;
}
}
委托就是装载、传递函数的容器而已
可以用委托变量来存储函数或者传递函数
系统其实已经提供了很多委托给我们用
Action:没有返回值,参数提供了 0~16个委托给我们用
Func:有返回值,参数提供了 0~16个委托给我们用
二、事件
1.概念
- 事件是基于委托的存在
- 事件是委托的安全包裹
- 让委托的使用更具有安全性
- 事件是一种特殊的变量类型
2.事件的使用
// 申明语法:
// 访问修饰符 event 委托类型 事件名;
// 事件的使用:
// 1.事件是作为 成员变量存在于类中
// 2.委托怎么用 事件就怎么用
// 事件相对于委托的区别:
// 1.不能在类外部 赋值
// 2.不能在类外部 调用
// 注意:
// 它只能作为成员存在于类和接口以及结构体中
internal class Test
{
// 委托成员变量 用于存储 函数的
public Action myFun;
public Test() {
// 事件的使用和委托 一模一样 只是有些 细微的区别
myFun = TestFun;
myFun += TestFun;
myFun -= TestFun;
myFun();
myFun.Invoke();
myFun = null;
myEvent = TestFun;
myEvent += TestFun;
myEvent -= TestFun;
myEvent();
myEvent.Invoke();
myEvent = null;
}
// 事件成员变量 用于存储 函数的
public event Action myEvent;
public void DoEvent() {
if (myEvent != null) myEvent();
}
public void TestFun() {
Console.WriteLine("123");
}
}
// 为什么有事件
// 1.防止外部随意置空委托
// 2.防止外部随意调用委托
// 3.事件相当于对委托进行了一次封装 让其更加安全
#endregion
internal class Program
{
private static void Main(string[] args) {
Console.WriteLine("事件");
Test t = new Test();
// 委托可以在外部赋值
t.myFun = null;
t.myFun = TestFun;
t.myFun = t.myFun + TestFun;
t.myFun += TestFun;
// 事件是不能再外部赋值的
// t.myEvent = null;
// t.myEvent = TestFun;
// 虽然不能直接赋值 但是可以 加减 去添加移除记录的函数
t.myEvent += TestFun;
t.myEvent -= TestFun;
// 委托是可以在外部调用的
t.myFun();
t.myFun.Invoke();
// 事件不能再外部调用
// t.myEvent();
// 只能在类的内部去封装 调用
t.DoEvent();
Action a = TestFun;
// 事件 是不能作为临时变量在函数中使用的
// event Action ae = TestFun;
}
private static void TestFun() { }
}
三、匿名函数
// 基本语法
// delegate (参数列表)
// {
// //函数逻辑
// };
// 何时使用?
// 1.函数中传递委托参数时
// 2.委托或事件赋值时
// 使用
// 1.无参无返回
// 这样申明匿名函数 只是在申明函数而已 还没有调用
// 真正调用它的时候 是这个委托容器啥时候调用 就什么时候调用这个匿名函数
Action a = delegate { Console.WriteLine("匿名函数逻辑"); };
a();
// 2.有参
Action<int, string> b = delegate(int a, string b) {
Console.WriteLine(a);
Console.WriteLine(b);
};
b(100, "123");
// 3.有返回值
Func<string> c = delegate { return "123123"; };
Console.WriteLine(c());
// 4.一般情况会作为函数参数传递 或者 作为函数返回值
Test t = new Test();
Action ac = delegate { Console.WriteLine("随参数传入的匿名函数逻辑"); };
t.Dosomthing(50, ac);
// 参数传递
t.Dosomthing(100, delegate { Console.WriteLine("随参数传入的匿名函数逻辑"); });
// 返回值
Action ac2 = t.GetFun();
ac2();
// 一步到位 直接调用返回的 委托函数
t.GetFun()();
// 匿名函数的缺点
// 添加到委托或事件容器中后 不记录 无法单独移除
Action ac3 = delegate { Console.WriteLine("匿名函数一"); };
ac3 += delegate { Console.WriteLine("匿名函数二"); };
ac3();
// 因为匿名函数没有名字 所以没有办法指定移除某一个匿名函数
// 此匿名函数 非彼匿名函数 不能通过看逻辑是否一样 就证明是一个
// ac3 -= delegate ()
// {
// Console.WriteLine("匿名函数一");
// };
ac3 = null;
// ac3();
四、lambda表达式
// 什么是lambad表达式
// 可以将lambad表达式 理解为匿名函数的简写
// 它除了写法不同外
// 使用上和匿名函数一模一样
// 都是和委托或者事件 配合使用的
// lambad表达式语法
// 匿名函数
// delegate (参数列表)
// {
// };
// lambad表达式
// (参数列表) =>
// {
// //函数体
// };
// 使用
// 1.无参无返回
Action a = () => { Console.WriteLine("无参无返回值的lambad表达式"); };
a();
// 2.有参
Action<int> a2 = value => { Console.WriteLine("有参数Lambad表达式{0}", value); };
a2(100);
// 3.甚至参数类型都可以省略 参数类型和委托或事件容器一致
Action<int> a3 = value => { Console.WriteLine("省略参数类型的写法{0}", value); };
a3(200);
// 4.有返回值
Func<string, int> a4 = value => {
Console.WriteLine("有返回值有参数的那么大表达式{0}", value);
return 1;
};
Console.WriteLine(a4("123123"));
// 其它传参使用等和匿名函数一样
// 缺点也是和匿名函数一样的
五、闭包
闭包是一种语言特性,它允许在函数内部定义的函数访问外部函数的局部变量。即使外层函数执行已终止。
在C#中我们可以使用Lambda来实现闭包闭包本质是一个对象(编译后),但使用上它和方法一致。使用闭包我们就可以实现拥有私有状态的函数!
定义
我们把在Lambda表达式(或匿名方法)中所引用的外部变量称为捕获变量。而捕获变量的表达式就称为闭包。
捕获变量
捕获的变量会在真正调用委托时“赋值”,而不是在捕获时“赋值”,即总是使用捕获变量的最新的值。
// 闭包
// 内层的函数可以引用包含在它外层的函数的变量
// 即使外层函数的执行已经终止
// 注意:
// 该变量提供的值并非变量创建时的值,而是在父函数范围内的最终值。
internal class Test
{
public Test() {
int value = 10;
action = () => { Console.WriteLine(value); };
// 方法体加+字段 这里就形成了闭包
// 因为 当构造函数执行完毕时 其中申明的临时变量value的声明周期被改变了
for (int i = 0; i < 10; i++) {
// 此index 非彼index
int index = i;
action += () => { Console.WriteLine(index); };
}
}
public event Action action;
public void DoSomthing() {
action();
}
}
static void Main(string[] args)
{
GetAction()(30);
}
static Action<int> GetAction()
{
int arg = 10;
Action<int> action = x =>
{
Console.WriteLine(x + arg);
};
action(10);
arg = 20;
action(20);
arg = 30;
return action;
}
output:
20
40
60