C#【进阶】委托和事件

news2024/11/15 10:38:28

委托和事件

在这里插入图片描述

文章目录

      • 1、委托
        • 1、委托概念
        • 2、基本语法
        • 3、定义自定义委托
        • 4、使用自定义委托
        • 5、委托变量可以存储多个函数
        • 6、系统定义好的委托
          • 思考 怪物死亡数据更新
      • 2、事件
        • 1、事件概念
        • 2、事件的使用
        • 3、为什么有事件
          • 思考 热水器
      • 3、匿名函数
        • 1、匿名函数概念
        • 2、基本语法
        • 3、使用
        • 4、匿名函数的缺点
          • 思考 匿名函数作为返回值
      • 4、Lambad表达式
        • 1、Lambad表达式概念
        • 2、lambad表达式语法
        • 3、使用
        • 4、闭包

1、委托

1、委托概念
委托是方法的容器,表示方法的变量类型
用来存储、传递方法
委托的本质是一个类,用来定义方法的类型(返回值和参数的类型)
不同的方法必须对应和各自格式一致的委托
2、基本语法
访问修饰符 delegate 返回值 委托名(参数列表);
3、定义自定义委托
访问修饰符不写默认为public
    声明了一个可以用来存储无参无返回值函数的容器
    delegate void MyFun();
    //表示用来装载或传递返回值为int有一个int参数的函数委托
    delegate int MyFu2(int x);
4、使用自定义委托
委托变量是函数的容器
    委托常用在:
    1、作为类的成员
    2、作为函数的参数
    
    static void Fun()
    {
        Console.WriteLine("aaa");
    }
    //声明委托方法1、将Fun()方法存到MyFun委托容器中
    MyFun f = new MyFun(Fun);
    //方法1、调用委托
    f.Invoke();
    f();
    //声明委托方法2、声明委托变量f2存储Fun()方法
    MyFun f2 = Fun;
    //方法2、调用委托
    f2();
    f2.Invoke();

    static int Fun2(int value)
    {
        return value;
    }

    MyFun2 f3 = Fun2;
    Console.WriteLine(f3(1));

    MyFun2 f4 = new MyFun2(Fun2);
    Console.WriteLine(f4.Invoke(3));

//声明委托
delegate void MyFun();
//表示用来装载或传递返回值为int有一个int参数的函数委托
delegate int MyFun2(int x);
5、委托变量可以存储多个函数
增加函数
    MyFun ff = Fun;
    ff += Fun;
    ff();

    class test{
    	MyFun fun;
        MyFun2 fun2;
	
    	public void AddFun(MyFun fun, MyFun2 fun2){
            this.fun += fun;
            this.fun2 += fun2;
        }
	}
移除函数
    ff -= Fun;
	//清空
	ff = null;
	if(ff!=null){
        ff();
    }
6、系统定义好的委托
无参无返回值 // Action
Action action = Fun;
有参无返回值 // Action<>
Action<int,string> action2 = Fun;
指定返回值类型	// Func<>
Func<string> func = Fun;
有参数有返回值 //Func<参数,返回值>
Func<int,int> func = Fun;
思考 怪物死亡数据更新
//怪物死亡后,玩家要加10元钱,界面要更新数据
//成就要累加怪物击杀数,用委托实现这些功能

Monster monster = new Monster();
Player player = new Player();
Panel panel = new Panel();
ChengJiu chengJiu = new ChengJiu();

//委托关联
monster.deadDoSomthing += player.MonsterDeadDoSomthing;
monster.deadDoSomthing += panel.MonsterDeadDoSomthing;
monster.deadDoSomthing += chengJiu.MonsterDeadDoSomthing;
monster.Dead();

class Monster
{
    //声明委托
    public Action<Monster> deadDoSomthing;
    public int money;
    public void Dead()
    {
        Random random = new Random();
        money = random.Next(15, 21);
        Console.WriteLine("怪物死亡");
        //使用委托 这里的this是将自己作为参数传出去
        if (deadDoSomthing != null) {  deadDoSomthing(this); }
        deadDoSomthing = null;
    }
}
class Player
{
    private int myMoney = 0;
    public void MonsterDeadDoSomthing(Monster m)
    {
        myMoney += m.money;
        Console.WriteLine("击杀获得{0}金币" , m.money);
        Console.WriteLine("余额"+myMoney);
        
    }
}
class Panel
{
    private int nowShowMoney = 0;
    public void MonsterDeadDoSomthing(Monster m)
    {
        nowShowMoney += m.money;
        Console.WriteLine("显示余额" + nowShowMoney);
    }
}

class ChengJiu
{
    private int nowKillMonsterNum = 0;
    public void MonsterDeadDoSomthing(Monster m)
    {
        nowKillMonsterNum++;
        Console.WriteLine("击杀数量" + nowKillMonsterNum);
    }
}

2、事件

1、事件概念
事件是基于委托的存在
事件是委托的安全包裹
让委托的使用更具有安全性
事件是一种特殊的变量类型
2、事件的使用
声明语法:
	访问修饰符 event 委托类型 事件名;
事件的使用:
	1、事件是作为成员变量存在于类中
	2、委托怎么用,事件就怎么用
事件相对于委托的区别:
	1、不能在类外部赋值,但可以加减
	2、不能在类外部调用
注意:
	它只能作为成员存在于类、接口以及结构体中
    不能作为临时变量
    
Test test = new Test();
class Test
{
    //委托成员变量,用于存储函数
    public Action myFunc;
    //事件成员变量,用于存储函数
    public event Action myEvent;
    
    public Test()
    {
        myFunc = TestFunc;
        myFunc += TestFunc;

        myEvent = TestFunc;
        myEvent += TestFunc;
        myEvent();
        myEvent.Invoke();
        myEvent = null;
    }
    public void TestFunc(){}
}
3、为什么有事件
1、防止外部随意置空委托
2、防止外部随意调用委托
3、事件相当于对委托进行了一次封装,让其更加安全
思考 热水器
//有一个热水器,包含一个加热器,一个报警器,一个显示器
//我们给热水通上电,当水温超过95度时
//1、报警器会开始发出语音,告诉你水温
//2、显示器也会改变水温提示,提示水已经烧开

Heater h = new Heater();
Alarm alarm = new Alarm();
Display display = new Display();

h.myEvent += alarm.ShowInfo;
h.myEvent += display.ShowInfo;
h.AddHot();
class Heater
{
    public event Action<int> myEvent;
    private int temperature = 25;
    public void AddHot()
    {
         int updateIndex= 0;
        while (true)
        {
            if (updateIndex % 9999999 == 0)
            {
                temperature++;
                Console.WriteLine("温度计"+temperature);
                if (temperature >= 95)
                {
                    if (myEvent!=null)
                    {
                        myEvent(temperature);
                    }
                    myEvent = null;
                }
                if (temperature >=100)
                {
                    break;
                }
                updateIndex = 0;
            }
            updateIndex++;
        }
    }
}
class Alarm
{
    public void ShowInfo(int v)
    {
        Console.WriteLine("滴滴,温度" + v);
    }
}
class Display
{
    public void ShowInfo(int v)
    {
        Console.WriteLine("显示温度" + v);
    }
}

3、匿名函数

1、匿名函数概念
配合委托和事件使用
脱离委托和事件,是不会使用匿名函数的
2、基本语法
delegate(参数列表){函数逻辑};
何时使用
	1、函数中传递委托参数时
	2、委托或事件赋值时
3、使用
//1、无参无返回值
Action action = delegate ()
{
    Console.WriteLine("匿名函数");
};
action();
//2、有参
Action<int,string> b = delegate (int a, string b)
{
    Console.WriteLine(a);
    Console.WriteLine(b);
};
b(1,"a");
//3、有返回值
Func<string> c = delegate ()
{
    return "a";
};
Console.WriteLine(c());
//4、一般情况会作为函数参数传递,或者作为函数返回值
Test t = new Test();
//参数传递
t.Do(1, delegate () { Console.WriteLine("随参数传入的匿名函数逻辑"); });
//返回值
Action ac = t.GetFun();
ac();
//直接调用返回的委托函数
t.GetFun()();
class Test
{
    public Action action;
    //作为参数传递时
    public void Do(int a , Action fun)
    {
        Console.WriteLine(a);
        fun();
    }
    //作为返回值
    public Action GetFun()
    {
        return delegate () { Console.WriteLine("函数内部返回的一个匿名函数逻辑"); };
    }
}
4、匿名函数的缺点
添加到委托或事件容器中后,不记录无法单独移除
思考 匿名函数作为返回值
//写一个函数传入一个整数,返回一个函数
//之后执行这个匿名函数时传入一个整数和之前那个函数传入的数相乘
//返回结果
Func<int, int> func = TestFunc(2);
Console.WriteLine(func(3));
Func<int, int> TestFunc(int i)
{
    //会改变i的生命周期
    return delegate(int v) {
        return i * v; 
    };
}

4、Lambad表达式

1、Lambad表达式概念

匿名函数的简写

2、lambad表达式语法
(参数列表) =>{};
3、使用
//1、无参无返回值
Action a = () =>
{
    Console.WriteLine("无参无返回值");
};
a();
//2、有参无返回值
Action<int> a2 = (int value) =>
{
    Console.WriteLine("有参无返回值" + value);
};
a2(2);
//3、省略参数类型,参数类型和委托或事件容器一致
Action<int> a3 = (value) =>
{
    Console.WriteLine("省略参数写法" + value);
};
a3(3);
//有参有返回值
Func<string, int> a4 = (value) =>
{
    Console.WriteLine("有参有返回值lambad" + value);
    return 1;
};
Console.WriteLine(a4("a"));
4、闭包
内层函数可以引用包含在它外层函数的变量
即使外层函数的执行已经终止
注意:
	该变量提供的值并非变量创建时的值,而是在父函数范围内的最终值
    
Test t = new Test();
t.DoAction();
class Test
{
    public event Action action;
    public Test()
    {
        //闭包
        int value =10;
        action = () =>
        {
            Console.WriteLine("闭包的值"+value);
        };

        for (int i = 0; i < 10; i++)
        {
            //每次循环的index不一样,此非彼
            int index = i;
            action += () =>
            {
                Console.WriteLine();
                Console.WriteLine("假循环"+i);//最终值
                Console.WriteLine("真循环" + index);
            };
        }
    }
    public void DoAction()
    {
        action();
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1679620.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

27_Scala功能函数

文章目录 功能函数1.功能函数处理集合数据2.扁平化操作3.按照指定条件将数据集中的数据进行过滤4.集合通过 自定义函数进行分组5.mapValues6.sortBy函数 功能函数 1.功能函数处理集合数据 –集合的功能函数 map List --> map( logical ) --> newList–实现一个不确定的…

Blender雕刻建模流程

1.构形 先构造一个大致相像的外形 可使用的方法包含 -多边形&#xff1a;表面细分&#xff0c;布尔 -曲线&#xff1a;曲线倒角 -融球&#xff08;使用较少&#xff09; -曲面&#xff08;使用较少&#xff09; 构形之后的准备 -应用缩放 -应用修改器 -曲线转网格 1.1…

8个迹象表明你需要一台新笔记本电脑,看一下你的笔记本是否有其中一个

序言 当你第一次打开你的笔记本电脑的盒子时,它会以最高性能运行,电池寿命更长,过热最小,资源使用效率高。然而,随着笔记本电脑的老化,它将不能满足预期用途。以下几个迹象表明,可能是时候寻找并投资一款新设备了。 你的设备不再具有预期用途 如果你的笔记本电脑不再…

战网国际服注册教程 暴雪战网国际服账号注册一站式教程分享

战网国际版&#xff0c;也即Battle.net环球版&#xff0c;是由暴雪娱乐操刀的全球化游戏交流枢纽&#xff0c;它突破地理限制&#xff0c;拥抱全世界的游戏玩家。与仅限特定地区的版本不同&#xff0c;国际版为玩家开辟了无障碍通道&#xff0c;让他们得以自由探索暴雪庞大游戏…

MacOS docker 安装与配置

orbstack 安装 官网&#xff1a; https://orbstack.dev 下载链接&#xff1a;Download OrbStack Fast, light, simple Docker Desktop alternative 选择是Apple M系列处理器&#xff0c; 或 Intel系列处理器 到这里就安装好了Orbstack软件&#xff0c;下面开始配置docker 下…

C语言/数据结构——栈的实现

一.前言 今天我们讲解新的领域——栈。 二.正文 1.栈 1.1栈的概念及结构 栈&#xff1a;一种特殊的线性表&#xff0c;其允许在固定的一段进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶&#xff0c;另一端称为栈底。栈中的数据元素遵守后进先出LIFO&#…

css笔记总结2

找到所有的 h1 标签。 选择器&#xff08;选对人&#xff09; 设置这些标签的样式&#xff0c;比如颜色为红色&#xff08;做对事&#xff09;。 ##css基础选择器 基础选择器又包括&#xff1a;标签选择器、类选择器、id 选择器和通配符选择器 ###标签选择器&#xff1a; 标签…

PostgreSQL(十二)报错:Tried to send an out-of-range integer as a 2-byte value: 51000

目录 一、报错场景二、源码分析三、实际原因&#xff08;更加复杂&#xff09;四、解决思路 一、报错场景 今天写了一个历史数据处理程序&#xff0c;在开发环境、测试环境都可以正常执行&#xff0c;但是放到生产环境上就不行&#xff0c;报了一个这样的错误&#xff1a; or…

【SpringBoot】SpringBoot整合jasypt进行重要数据加密

&#x1f4dd;个人主页&#xff1a;哈__ 期待您的关注 目录 &#x1f4d5;jasypt简介 &#x1f525;SpringBoot使用jasypt &#x1f4c2;创建我需要的数据库文件 &#x1f4d5;引入依赖 &#x1f513;配置数据库文件&#xff08;先不进行加密&#xff09; &#x1f319;创…

十二.吊打面试官系列-JVM优化-深入JVM内存模型

JVM内存模型 1.JVM的组成 整个JVM组成由 &#xff1a;运行时数据区 &#xff0c; 类加载子系统 &#xff0c; 执行引擎 &#xff0c; 本地方法库 几部分组成 上面是Java7的内存模型&#xff0c;Java8以后做了一些调整&#xff0c;把方法区变成了元空间&#xff0c;元空间不在…

Linux 第三十三章

&#x1f436;博主主页&#xff1a;ᰔᩚ. 一怀明月ꦿ ❤️‍&#x1f525;专栏系列&#xff1a;线性代数&#xff0c;C初学者入门训练&#xff0c;题解C&#xff0c;C的使用文章&#xff0c;「初学」C&#xff0c;linux &#x1f525;座右铭&#xff1a;“不要等到什么都没有了…

Golang | Leetcode Golang题解之第80题删除有序数组中的重复项II

题目&#xff1a; 题解&#xff1a; func removeDuplicates(nums []int) int {n : len(nums)if n < 2 {return n}slow, fast : 2, 2for fast < n {if nums[slow-2] ! nums[fast] {nums[slow] nums[fast]slow}fast}return slow }

1、sql server数据库进行sql注入

靶机取自&#xff1a;墨者sql server 1、判断数据库类型 抓包知sql server&#xff0c;所以注入语句跟MySQL有些区别 2、判断注入点 “http://219.153.49.228:42514/new_list.asp?id2 ”&#xff0c;当id2 and 11时显示正确&#xff0c;id2 and 12时页面报错。 3、确定列…

Vue.js的发展史(一)

Vue.js的发展史&#xff08;一&#xff09; 什么是Vue? Vue (发音为 /vjuː/&#xff0c;类似 view) 是一款用于构建用户界面的 JavaScript 框架。它基于标准 HTML、CSS 和 JavaScript 构建&#xff0c;并提供了一套声明式的、组件化的编程模型&#xff0c;帮助你高效地开发…

云服务器和物理机该怎样分别呢

随着网络的不断发展&#xff0c;服务器的类型也在以不同的方式更新。现在云服务器的兴起占据了很大一部分市场&#xff0c;物理机的市场份额受到了很大的冲击。物理机和云服务器有什么区别&#xff1f;如何选择适合自己需求的&#xff1f;虽然物理服务器和云服务器都是服务器&a…

Python进度条工具——tqdm

原文链接&#xff1a;http://www.juzicode.com/python-note-tqdm 在安装Python库文件的时候我们经常可以看到这种进度条&#xff1a; 其实Python库中就自带了现成的工具库——tqdm。 tqdm读起来比较拗口&#xff0c;它是从“进程”的阿拉伯语taqaddum简化而来。 安装tqdm 使用…

有关登录安全,测试人该知道些什么?

作为测试&#xff0c;给我们一个关键词“登录”&#xff0c;我们可能想到的用例设计更多的是什么用户名、密码校验是否合法、是否为空、是否正确等等之类的场景。 但在如今信息化的时代&#xff0c;“登录安全”已经是一个很热门且普遍的的话题了&#xff0c;今天给大家简单分…

uniapp小程序控制页面元素滚动指定距离

要实现页面元素滚动&#xff0c;最好还是使用 scroll-view 来实现&#xff0c;官方文档地址&#xff1a;scroll-view | uni-app官网 通过设置scroll事件来实现滚动监听&#xff0c;当滚动的元素的时候&#xff0c;就会触发这个事件&#xff0c;并且事件里面包含有滚动距离&…

哈希表的理解和实现

目录 1. 哈希的概念 (是什么) 2. 实现哈希的两种方式 (哈希函数) 2.1. 直接定址法 2.2. 除留余数法 2.2.1. 哈希冲突 3. 补充知识 3.1. 负载因子 3.2. 线性探测和二次探测 4. 闭散列实现哈希表 (开放定址法) 4.1. 开放定址法的实现框架 4.2. Xq::hash_table::insert…