• C#进阶04——委托和事件


    1.委托

    1.委托是什么

    委托是 函数(方法)的容器 

    可以理解为表示函数(方法)的变量类型

    用来 存储、传递函数(方法)

    委托的本质是一个类,用来定义函数(方法)的类型(返回值和参数的类型)

    不同的 函数(方法)必须对应和各自"格式"一致的委托

    2.基本语法

    关键字 : delegate
    语法:访问修饰符 delegate 返回值 委托名(参数列表);

    写在哪里?
    可以申明在namespace和class语句块中
    更多的写在namespace中

    简单记忆委托语法 就是 函数申明语法前面加一个delegate关键字

    3.定义自定义委托 

    访问修饰符默认不写 为public 在别的命名空间中也能使用

    private 其他命名空间就不能用了

    一般使用public

    申明了一个可以用来存储无参无返回值的容器

    这里只是定义了规则 并没有使用

    delegate void MyFun();

    委托规则的申明 是不能重名(同一语句块中)

    表示用来装载或传递 返回值为 int 有一个 int参数 的函数的 委托 容器

    delegate int MyFun2(int a);

    1. class Program
    2. {
    3. static void Main(string[] args)
    4. {
    5. Console.WriteLine("委托");
    6. //专门用来装载 函数的 容器
    7. MyFun f = new MyFun(Fun);
    8. Console.WriteLine("1");
    9. Console.WriteLine("2");
    10. Console.WriteLine("3");
    11. Console.WriteLine("4");
    12. Console.WriteLine("5");
    13. f.Invoke(); //1 2 3 4 5 123456
    14. MyFun f2 = Fun;
    15. Console.WriteLine("1");
    16. Console.WriteLine("2");
    17. Console.WriteLine("3");
    18. Console.WriteLine("4");
    19. Console.WriteLine("5");
    20. f2(); //1 2 3 4 5 123456
    21. MyFun2 f3 = Fun2;
    22. f3(1);
    23. Console.WriteLine(f3(1)); //1
    24. MyFun2 f4 = new MyFun2(Fun2);
    25. Console.WriteLine(f4.Invoke(3)); //3
    26. }
    27. static void Fun()
    28. {
    29. Console.WriteLine("123456");
    30. }
    31. static int Fun2(int value)
    32. {
    33. return value;
    34. }
    35. }

    委托是支持 泛型的 可以让返回值和参数 可变 更方便我们的使用

    delegate T MyFun3(T t, K k);

    4.使用定义好的委托

    委托变量是函数的容器

    委托常用在:

    1. 作为类的成员
    2. 作为函数的参数
    1. class Test
    2. {
    3. public MyFun fun;
    4. public MyFun2 fun2;
    5. public void TestFun(MyFun fun, MyFun2 fun2)
    6. {
    7. //先处理一些别的逻辑 当这些逻辑处理完了 再执行传入的函数
    8. int i = 1;
    9. i *= 2;
    10. i += 2;
    11. //fun();
    12. //fun2(i);
    13. //this.fun = fun;
    14. //this.fun2 = fun2;
    15. }
    16. #region
    17. public void AddFun(MyFun fun, MyFun2 fun2)
    18. {
    19. this.fun += fun;
    20. this.fun2 += fun2;
    21. }
    22. #endregion
    23. #region
    24. public void Remove(MyFun fun, MyFun2 fun2)
    25. {
    26. //this.fun = this.fun - fun;
    27. this.fun -= fun;
    28. this.fun2 -= fun2;
    29. }
    30. }

     5.委托变量可以存储多个函数(多播委托)

    1. //如何用委托存储多个函数
    2. MyFun ff = null;
    3. //ff = ff + Fun;
    4. ff += Fun;
    5. ff += Fun3;
    6. ff();
    7. //从容器中移除指定的函数
    8. ff -= Fun;
    9. //多减 不会报错 无非就是不处理而已
    10. ff -= Fun;
    11. ff();
    12. //清空容器
    13. ff = null;
    14. if( ff != null )
    15. {
    16. ff();
    17. }

    6.系统定义好的委托

    使用系统自带委托 需要引用 using System;

    1. //无参无返回值
    2. Action action = Fun;
    3. action += Fun3;
    4. action();
    5. //可以指定返回值类型的 泛型委托
    6. Func<string> funString = Fun4;
    7. Func<int> funInt = Fun5;
    8. //可以传n个参数的 系统提供了 1到16个参数的委托
    9. Action<int, string> action1 = Fun6;
    10. //可以传n个参数的 并且有返回值的 系统也提供了 16个委托
    11. Func<int, int> func2 = Fun2;
    12. //***************************************************//
    13. static string Fun4()
    14. {
    15. return " ";
    16. }
    17. static int Fun5()
    18. {
    19. return 1;
    20. }
    21. static void Fun6(int i, string s)
    22. {
    23. }

    7.总结

    简单理解 委托 就是装载、传递函数的容器而已

    可以用委托变量 来存储函数或者传递函数的

    系统其实已经提供了很多委托给我们用

    Action :没有返回值,参数 提供了 0到16个委托给我们用

    Func   :有返回值,参数 提供了0到16个委托给我们用

    2.事件

    1.事件是什么 

    事件是基于委托的存在

    事件是委托的安全包裹

    让委托的使用更具有安全性

    事件 是一种特殊的变量类型

    2.事件的使用

    申明语法:
    访问修饰符 event 委托类型 事件名;


    事件的使用:
    1.事件是作为 成员变量存在于类中
    2.委托怎么用 事件就怎么用


    事件相对于委托的区别:
    1.不能在类外部 赋值
    2.不能再类外部 调用
    注意:它只能作为成员存在于类和接口以及结构体中

    1. class Test
    2. {
    3. //委托成员变量 用于存储 函数的
    4. public Action myFun;
    5. //事件成员变量 用于存储 函数的
    6. public event Action myEvent;
    7. public Test()
    8. {
    9. //事件的使用和委托 一模一样 只是有些 细微的区别
    10. myFun = TestFun;
    11. myFun += TestFun;
    12. myFun -= TestFun;
    13. myFun();
    14. myFun.Invoke();
    15. //myFun = null;
    16. myEvent = myFun;
    17. myEvent += myFun;
    18. myEvent -= myFun;
    19. myEvent();
    20. myEvent.Invoke();
    21. //myEvent = null;
    22. }
    23. public void TestFun()
    24. {
    25. Console.WriteLine("123");
    26. }
    27. }
    28. class Program
    29. {
    30. static void Main(string[] args)
    31. {
    32. Console.WriteLine("事件");
    33. Test t = new Test();
    34. //委托可以在外部赋值
    35. t.myFun = null;
    36. //事件不能在外部赋值
    37. //t.myEvent = null;
    38. //虽然不能直接赋值 但是可以 加减 去添加移除记录的函数
    39. t.myEvent += TestFun;
    40. //委托是可以在外部调用的
    41. t.myFun();
    42. t.myFun.Invoke();
    43. //事件不能在外部调用
    44. //t.myEvent();
    45. //t.myEvent.invoke();
    46. //只能在类的内部去封装 调用
    47. Action a = TestFun;
    48. //event Action e = TestFun;
    49. //事件 是不能作为临时变量在函数中使用的
    50. }
    51. static void TestFun()
    52. {
    53. Console.WriteLine("123");
    54. }
    55. }

    3.为什么有事件 

    1. 防止外部随意置空委托
    2. 防止外部随意调用委托
    3. 事件相当于对委托进行了一次封装 让其更加安全

    4.总结

    事件和委托的区别

    事件和委托的使用基本是一模一样的

    事件就是特殊的委托

    主要区别:

    1. 事件不能再外部使用赋值=符号,只能使用+ - 委托 哪里都能用
    2. 事件 不能再外部执行 委托哪里都能执行
    3. 事件 不能作为 函数中的临时变量的 委托可以 

    3.匿名函数

    1.什么是匿名函数

    顾名思义,就是没有名字的函数

    匿名函数的使用主要是配合委托和事件进行使用

    脱离委托和事件 是不会使用匿名函数的 

    2.基本语法

    delegate (参数列表)
    {
        //函数逻辑
    };
    何时使用?
    1.函数中传递委托参数时
    2.委托或事件赋值时

    3.使用

    1.无参无返回

    1. 这样申明匿名函数 只是在申明函数而已 还没有调用
    2. 真正调用它的时候 是这个委托容器啥时候调用 就申明时候调用这个匿名函数
    1. Action a= delegate ()
    2. {
    3. Console.WriteLine("匿名函数逻辑");
    4. };
    5. a();

     2.有参

    1. Action<int,string>b= delegate (int a, string b)
    2. {
    3. Console.WriteLine(a);
    4. Console.WriteLine(b);
    5. };
    6. b(100,"123");

    3.有返回

    1. Func<string> c= delegate ()
    2. {
    3. return "123";
    4. };
    5. Console.WriteLine(c());

    4.一般情况会作为函数参数传递 或者 作为函数返回值

    1. Test t = new Test();
    2. Action ac = delegate ()
    3. {
    4. Console.WriteLine("随参数传入的匿名函数逻辑");
    5. };
    6. t.Dosomething(50, ac);
    7. // 参数传递
    8. t.Dosomething(100, delegate ()
    9. {
    10. Console.WriteLine("随参数传入的匿名函数逻辑");
    11. });
    12. // 返回值
    13. Action ac2 = t.GetFun();
    14. ac2();
    15. //一步到位 直接调用返回的 委托函数
    16. t.GetFun()();

    4.匿名函数的缺点

    添加到委托或事件中后 不记录 无法单独移除

    1. Action ac3 = delegate ()
    2. {
    3. Console.WriteLine("匿名函数一");
    4. };
    5. ac3+= delegate ()
    6. {
    7. Console.WriteLine("匿名函数二");
    8. };
    9. ac3();
    10. //因为匿名函数没有名字 所以没有办法指定移除某一个匿名函数
    11. //此匿名函数非彼匿名函数 不能通过看逻辑是否一样 就证明是一个
    12. //ac3 -= delegate ()
    13. //{
    14. // Console.WriteLine("匿名函数一");
    15. //};
    16. ac3 = null;
    17. //ac3();

    5.总结

    匿名函数 就是没有名字的函数

    固定写法:

    delegate (参数列表){}

    主要是在 委托传递和存储时  为了方便可以直接使用倪敏该函数

    缺点是 没有办法指定移除

    4.lambad表达式

    1.什么是lambad表达式

    可以将lambad表达式 理解为匿名函数的简写

    它除了写法不同外

    使用上和匿名函数一模一样

    都是和委托或者事件 配合使用的 

    2.lambad表达式语法

    匿名函数
    delegate (参数列表)
    {

    };

    lambad表达式
    (参数列表) =>
    {
        //函数体
    };

    3.使用

    1.无参无返回

    1. Action a = () =>
    2. {
    3. Console.WriteLine("无参无返回值的lambad表达式");
    4. };
    5. a();

     2.有参

    1. Action<int>a2= (int value) =>
    2. {
    3. Console.WriteLine("有参数的lambda表达式{0}",value);
    4. };
    5. a2(100);

    3.甚至参数类型都可以省略 参数类型和委托或事件容器一致

    1. Action<int> a3 = (value) =>
    2. {
    3. Console.WriteLine("省略参数类型的写法{0}", value);
    4. };
    5. a3(200);

    4.有返回值

    1. Func<string, int> a4 = (value) =>
    2. {
    3. Console.WriteLine("有返回值有参数的lambda表达式{0}", value);
    4. return 1;
    5. };
    6. //其他传承使用的和匿名函数一样
    7. //确定也是和匿名函数一样的

    4.闭包

    内层的函数可以引用包含在它外层的函数的变量

    即使外层函数的执行已经终止

    注意:该变量提供的值并非变量创建时的值,而是在父函数范围内的最终值

    1. class Test
    2. {
    3. event Action action;
    4. public Test()
    5. {
    6. int value = 10;
    7. //这里就形成了闭包
    8. //因为 当构造函数执行完毕时 其中什么的临时变量value的生命周期被改变了
    9. action = () =>
    10. {
    11. Console.WriteLine(value);
    12. };
    13. for (int i = 0; i < 10; i++)
    14. {
    15. //此index 非彼index
    16. int index = i;
    17. action += () =>
    18. {
    19. Console.WriteLine(index);
    20. };
    21. }
    22. }
    23. public void DoSomething()
    24. {
    25. action();
    26. }
    27. }

     5.总结

    匿名函数的特殊写法 就是 lambad表达式

    固定写法 就是(参数列表) =>{}

    参数列表 可以直接省略参数类型

    主要在 委托传递和存储时  为了方便可以直接使用匿名函数或者lambad表达式

    缺点:无法指定移除

  • 相关阅读:
    java毕业设计项目_第167期ssm多用户博客个人网站_计算机毕业设计
    Android进行字符串替换
    GPU编程基础-CUDA实现图像处理
    pytorch 配置deformabledetr和referformer工程环境踩坑
    sqli第一关
    Java编程简便技巧篇(二)——字母和数字之间的转换
    第三站:函数(第一幕)
    java编程计算机网页项目jsp高校体育竞技赛系统myeclipse开发Mysql数据库web结构
    基于压缩感知的磁共振成像重建算法研究
    决策树--ID3算法
  • 原文地址:https://blog.csdn.net/weixin_45274937/article/details/126622848