C#【进阶】委托和事件

发布于:2024-05-16 ⋅ 阅读:(70) ⋅ 点赞:(0)

委托和事件

在这里插入图片描述

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