当前位置: 首页 > news >正文

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);

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("委托");
 
            //专门用来装载 函数的 容器
            MyFun f = new MyFun(Fun);
            Console.WriteLine("1");
            Console.WriteLine("2");
            Console.WriteLine("3");
            Console.WriteLine("4");
            Console.WriteLine("5");
            f.Invoke();   //1  2  3  4  5  123456
 
            MyFun f2 = Fun;
            Console.WriteLine("1");
            Console.WriteLine("2");
            Console.WriteLine("3");
            Console.WriteLine("4");
            Console.WriteLine("5");
            f2();      //1  2  3  4  5  123456
 
            MyFun2 f3 = Fun2;
            f3(1);
            Console.WriteLine(f3(1));    //1

            MyFun2 f4 = new MyFun2(Fun2);
            Console.WriteLine(f4.Invoke(3));   //3
        }
        static void Fun()
        {
            Console.WriteLine("123456");
        }
        static int Fun2(int value)
        {
            return value;
        }
    }

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

delegate T MyFun3<T, K>(T t, K k);

4.使用定义好的委托

委托变量是函数的容器

委托常用在:

  1. 作为类的成员
  2. 作为函数的参数
    class Test
    {
        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;
        }
 
        #region 增
        public void AddFun(MyFun fun, MyFun2 fun2)
        {
            this.fun += fun;
            this.fun2 += fun2;
        }
        #endregion
 
        #region 删
        public void Remove(MyFun fun, MyFun2 fun2)
        {
            //this.fun = this.fun - fun;
            this.fun -= fun;
            this.fun2 -= fun2;
        }
    }

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

           //如何用委托存储多个函数
            MyFun ff = null;
            //ff = ff + Fun;
            ff += Fun;
            ff += Fun3;
            ff();
            //从容器中移除指定的函数
            ff -= Fun;
            //多减 不会报错 无非就是不处理而已
            ff -= Fun;
            ff();
            //清空容器
            ff = null;
            if( ff != null )
            {
                ff();
            }

6.系统定义好的委托

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

            //无参无返回值
            Action action = Fun;
            action += Fun3;
            action();
 
            //可以指定返回值类型的 泛型委托
            Func<string> funString = Fun4;
            Func<int> funInt = Fun5;
 
            //可以传n个参数的 系统提供了 1到16个参数的委托
            Action<int, string> action1 = Fun6;
 
            //可以传n个参数的 并且有返回值的 系统也提供了 16个委托
            Func<int, int> func2 = Fun2;

//***************************************************//
        static string Fun4()
        {
            return " ";
        }
        static int Fun5()
        {
            return 1;
        }
        static void Fun6(int i, string s)
        {
            
        }

7.总结

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

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

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

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

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

2.事件

1.事件是什么 

事件是基于委托的存在

事件是委托的安全包裹

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

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

2.事件的使用

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


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


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

    class Test
    {
        //委托成员变量 用于存储 函数的
        public Action myFun;
        //事件成员变量 用于存储 函数的
        public event Action myEvent;
 
        public Test()
        {
            //事件的使用和委托 一模一样 只是有些 细微的区别
            myFun = TestFun;
            myFun += TestFun;
            myFun -= TestFun;
            myFun();
            myFun.Invoke();
            //myFun = null;
 
            myEvent = myFun;
            myEvent += myFun;
            myEvent -= myFun;
            myEvent();
            myEvent.Invoke();
            //myEvent = null;
        }
        public void TestFun()
        {
            Console.WriteLine("123");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("事件");
 
            Test t = new Test();
 
            //委托可以在外部赋值
            t.myFun = null;
 
            //事件不能在外部赋值
            //t.myEvent = null;
            //虽然不能直接赋值 但是可以 加减 去添加移除记录的函数
            t.myEvent += TestFun;
 
            //委托是可以在外部调用的
            t.myFun();
            t.myFun.Invoke();
 
            //事件不能在外部调用
            //t.myEvent();
            //t.myEvent.invoke();
            //只能在类的内部去封装 调用
 
            Action a = TestFun;
            //event Action e = TestFun;
            //事件 是不能作为临时变量在函数中使用的
        }
        static void TestFun()
        {
            Console.WriteLine("123");
        }
    }

3.为什么有事件 

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

4.总结

事件和委托的区别

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

事件就是特殊的委托

主要区别:

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

3.匿名函数

1.什么是匿名函数

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

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

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

2.基本语法

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

3.使用

1.无参无返回

  1. 这样申明匿名函数 只是在申明函数而已 还没有调用
  2. 真正调用它的时候 是这个委托容器啥时候调用 就申明时候调用这个匿名函数
            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 "123";
             };
            Console.WriteLine(c());

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

            Test t = new Test();
            Action ac = delegate ()
              {
                  Console.WriteLine("随参数传入的匿名函数逻辑");
              };
            t.Dosomething(50, ac);
            // 参数传递
            t.Dosomething(100, delegate ()
             {
                 Console.WriteLine("随参数传入的匿名函数逻辑");
             });
 
            // 返回值
            Action ac2 = t.GetFun();
            ac2();
            //一步到位 直接调用返回的 委托函数
            t.GetFun()();

4.匿名函数的缺点

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

            Action ac3 = delegate ()
            {
                Console.WriteLine("匿名函数一");
            };
            ac3+= delegate ()
            {
                Console.WriteLine("匿名函数二");
            };
            ac3();
            //因为匿名函数没有名字 所以没有办法指定移除某一个匿名函数
            //此匿名函数非彼匿名函数 不能通过看逻辑是否一样 就证明是一个
            //ac3 -= delegate ()
            //{
            //    Console.WriteLine("匿名函数一");
            //};
            ac3 = null;
            //ac3();

5.总结

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

固定写法:

delegate (参数列表){}

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

缺点是 没有办法指定移除

4.lambad表达式

1.什么是lambad表达式

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

它除了写法不同外

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

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

2.lambad表达式语法

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

};

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

3.使用

1.无参无返回

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

 2.有参

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

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


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

4.有返回值

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

4.闭包

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

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

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

    class Test
    {
        event Action action;
        public Test()
        {
            int value = 10;
            //这里就形成了闭包
            //因为 当构造函数执行完毕时 其中什么的临时变量value的生命周期被改变了
            action = () =>
              {
                  Console.WriteLine(value);
              };
            for (int i = 0; i < 10; i++)
            {
                //此index 非彼index
                int index = i;
                action += () =>
                  {
                      Console.WriteLine(index);
                  };
            }
        }
        public void DoSomething()
        {
            action();
        }
    }

 5.总结

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

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

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

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

缺点:无法指定移除

相关文章:

  • MySQL数据库 增删查改案例讲解
  • 【面试入门必刷】算法入门-数据结构-栈(一)
  • 《论文复现》MOJITALK: Generating Emotional Responses at Scale 部分过程讲解
  • GBase 8s 安全性(6)- 备份与恢复
  • 【人工智能】神经网络八股扩展
  • 如何获取大数据行业高薪岗位offer?
  • mac mongodb6.0.1安装
  • Spring常见问题解决 - @EnableWebMvc 导致自定义序列化器失效
  • 深入理解JVM(一)JVM与Java体系结构
  • 【数据分享】 中国五批3610个国家级非物质文化遗产空间分布数据
  • 7、JAVA入门——if选择结构
  • 【论文-目标追踪】BoT-SORT: Robust Associations Multi-Pedestrian Tracking
  • 【JetBrains】安装使用技巧
  • 进程间通信之有名(命名)管道、断言函数
  • iPayLinks艾贝盈金融科技,助力健身器材企业乘风破浪
  • “大数据应用场景”之隔壁老王(连载四)
  • 【面试系列】之二:关于js原型
  • 03Go 类型总结
  • ES6, React, Redux, Webpack写的一个爬 GitHub 的网页
  • ES6简单总结(搭配简单的讲解和小案例)
  • iBatis和MyBatis在使用ResultMap对应关系时的区别
  • Java 实战开发之spring、logback配置及chrome开发神器(六)
  • javascript从右向左截取指定位数字符的3种方法
  • Logstash 参考指南(目录)
  • Redis学习笔记 - pipline(流水线、管道)
  • Zsh 开发指南(第十四篇 文件读写)
  • 仿天猫超市收藏抛物线动画工具库
  • 记一次用 NodeJs 实现模拟登录的思路
  • 山寨一个 Promise
  • 协程
  • Salesforce和SAP Netweaver里数据库表的元数据设计
  • 格斗健身潮牌24KiCK获近千万Pre-A轮融资,用户留存高达9个月 ...
  • ​软考-高级-系统架构设计师教程(清华第2版)【第9章 软件可靠性基础知识(P320~344)-思维导图】​
  • #pragma multi_compile #pragma shader_feature
  • (附源码)node.js知识分享网站 毕业设计 202038
  • (附源码)ssm高校实验室 毕业设计 800008
  • (附源码)ssm码农论坛 毕业设计 231126
  • (规划)24届春招和25届暑假实习路线准备规划
  • (十)c52学习之旅-定时器实验
  • (十)T检验-第一部分
  • (十三)Java springcloud B2B2C o2o多用户商城 springcloud架构 - SSO单点登录之OAuth2.0 根据token获取用户信息(4)...
  • (四)JPA - JQPL 实现增删改查
  • (万字长文)Spring的核心知识尽揽其中
  • (一) springboot详细介绍
  • (一)Neo4j下载安装以及初次使用
  • (转)Spring4.2.5+Hibernate4.3.11+Struts1.3.8集成方案一
  • (转)关于如何学好游戏3D引擎编程的一些经验
  • (转)可以带来幸福的一本书
  • ***php进行支付宝开发中return_url和notify_url的区别分析
  • .md即markdown文件的基本常用编写语法
  • .NET delegate 委托 、 Event 事件
  • .net mvc actionresult 返回字符串_.NET架构师知识普及
  • @select 怎么写存储过程_你知道select语句和update语句分别是怎么执行的吗?
  • [ element-ui:table ] 设置table中某些行数据禁止被选中,通过selectable 定义方法解决
  • [100天算法】-每个元音包含偶数次的最长子字符串(day 53)