当前位置: 首页 > 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表达式

缺点:无法指定移除

相关文章:

  • vue项目设置打包后的静态文件访问路径
  • 2021兴业数金Java笔试题面经
  • 【无标题】产品经理基础--08交互说明撰写方法
  • 2021-05-13 Redis面试题 Redis集群最大节点个数是多少?
  • word内容和纸张方向一起旋转的方法
  • 深入理解计算机系统 csapp datalab 详解(位操作,数据表示)
  • flink 实时计算与RockDB状态存取的猜想
  • 1.5-39:与7无关的数
  • linux中开始mysql中binlog日志
  • 树——二叉查找树 - 有删除动作
  • idea远程拉取新项目
  • .NET 线程 Thread 进程 Process、线程池 pool、Invoke、begininvoke、异步回调
  • C++程序调试详解(包括打断点 单步调试 数据断点...)
  • 数据结构之图
  • Opencv项目实战:03 扫描二维码条形码
  • 实现windows 窗体的自己画,网上摘抄的,学习了
  • Android 初级面试者拾遗(前台界面篇)之 Activity 和 Fragment
  • Dubbo 整合 Pinpoint 做分布式服务请求跟踪
  • emacs初体验
  • ES6之路之模块详解
  • JavaScript 一些 DOM 的知识点
  • Javascript弹出层-初探
  • JS基础之数据类型、对象、原型、原型链、继承
  • mysql 5.6 原生Online DDL解析
  • PHP 小技巧
  • PHP那些事儿
  • win10下安装mysql5.7
  • 互联网大裁员:Java程序员失工作,焉知不能进ali?
  • 详解NodeJs流之一
  • 云大使推广中的常见热门问题
  • ionic异常记录
  • ​LeetCode解法汇总307. 区域和检索 - 数组可修改
  • ​Python 3 新特性:类型注解
  • #NOIP 2014#Day.2 T3 解方程
  • (007)XHTML文档之标题——h1~h6
  • (1)常见O(n^2)排序算法解析
  • (3)选择元素——(14)接触DOM元素(Accessing DOM elements)
  • (AtCoder Beginner Contest 340) -- F - S = 1 -- 题解
  • (顶刊)一个基于分类代理模型的超多目标优化算法
  • (多级缓存)缓存同步
  • (附源码)php投票系统 毕业设计 121500
  • (附源码)ssm旅游企业财务管理系统 毕业设计 102100
  • (含react-draggable库以及相关BUG如何解决)固定在左上方某盒子内(如按钮)添加可拖动功能,使用react hook语法实现
  • (区间dp) (经典例题) 石子合并
  • (十)【Jmeter】线程(Threads(Users))之jp@gc - Stepping Thread Group (deprecated)
  • (原創) 系統分析和系統設計有什麼差別? (OO)
  • (转)真正的中国天气api接口xml,json(求加精) ...
  • .NET BackgroundWorker
  • .NET 的程序集加载上下文
  • .NET 使用 ILMerge 合并多个程序集,避免引入额外的依赖
  • [ 隧道技术 ] cpolar 工具详解之将内网端口映射到公网
  • [04]Web前端进阶—JS伪数组
  • [2669]2-2 Time类的定义
  • [BZOJ] 2006: [NOI2010]超级钢琴
  • [C++] 统计程序耗时