C#中委托与事件

一、委托

在面向对象中,我们可以将任何数据类型作为参数传递给方法,能否将一个方法作为参数传递给另一个方法?C#中通过委托可以实现将方法作为参数进行传递。

1.1概念

  1. 委托是一种引用类型,它可以用于封装并传递方法作为参数。委托可以理解为是一个指向方法的**“指针”,它允许将方法作为参数传递给其他方法或存储在数据结构中,然后稍后调用这些方法。(委托可以看作时函数的容器)**

  2. 委托与类同级别
    在这里插入图片描述

  3. 委托需要有与之签名相同的方法与之对应

1.2目的作用

委托的目的就是将方法当作参数进行传递 ;
封装变化的请求

1.3定义方法的委托

  1. 委托:将方法作为参数进行传递 方法是有签名( 签名:返回值类型 ,参数列表(参数的个数 ,参数类型))。
  2. 委托需要和方法有相同的签名 也就是说委托和方法 要参数相同 返回值相同 你才可以引用这个方法。
  3. 访问修饰符:public protected internal…委托的访问修饰符一般都定义成public,因为我们的程序可能会涉及到跨程序集调用。
  4. 委托和类是平级的 不要定义在类里面
//01、声明一个委托
    public delegate void DelTest();

    internal class Program
    {
        static void Main(string[] args)
        {       
            //03.创建委托(对象),两种方式
            DelTest delTest = new DelTest(Test);//方法1
            
            //或者 创建委托(变量)可以省略new 关键字,直接将要传递的方法赋值即可,注意,方法在赋值的时候,不要写();
            //虽然我们没有创建委托对象(new出来的才叫对象),但是编译器在编译代码的时候还是去帮助我们new了委托对象
            DelTest delTest1 = Test;//方法2
            
            //04.调用委托,两种方式:
            delTest();//委托名字()
            delTest.Invoke();//调用(委托)对象里面存在的方法,委托名字.Invoke();
           // delTest1();
        }
        //02.定义方法
        public static void Test()
        {
            Console.WriteLine("没有参数没有返回值");
        }
    }
}

1.4匿名方法创建委托

  1. 当使用的方法只使用一次时,没必要单独创建一个方法,直接定义委托对象时创建匿名方法
DelTest delTest = delegate () { Console.WriteLine("没有返回值没有参数"); };//后面加封号
delTest();
  1. 匿名方法允许你在需要委托的地方直接定义方法体,而不需要显式地创建一个单独的方法。
//没有参数 没有返回值
DelTest1 delTest1 = delegate () { Console.WriteLine("没有参数 没有返回值"); };
delTest1();

//没有参数  有返回值
DelTest2 delTest2 = delegate () { return "没有参数  有返回值"; };
Console.WriteLine(delTest2());

//有参数 没有返回值
DelTest3 delTest3 = delegate (string str) { Console.WriteLine("有参数 没有返回值"+str); };
delTest3("delTest3");

//有参数  有返回值
DelTest4 delTest4 = delegate (int n1,int n2) { return n1 + n2; };
Console.WriteLine(delTest4(10,20));



//没有参数 没有返回值
public delegate void DelTest1();
//没有参数  有返回值
public delegate string DelTest2();
//有参数 没有返回值
public delegate void DelTest3(string name);
//有参数  有返回值
public delegate int DelTest4(int n1,int n2);

1.5委托+Lambda方式简化创建(常用)

  1. Lambda格式
(parameters参数) => expression表达式

其中,parameters是传递给委托的参数列表,可以是空的或包含多个参数;expression是一个表达式,它将计算并返回给委托调用者的值。
2. 创建委托:匿名函数+Lambda+委托

1.没有参数 没有返回值
1.1匿名方法
DelTest1 delTest1 = delegate () { Console.WriteLine("没有参数 没有返回值"); };
delTest1();

1.2lambda1
DelTest1 delTest11 = () => { Console.WriteLine("没有参数 没有返回值"); };
delTest11();
  1. 图中箭头指向的地方对应位置作用一样
    在这里插入图片描述
2.有参数 没有返回值

DelTest3 delTest3 = delegate (string name) { Console.WriteLine(name); };
delTest3("有参数 没有返回值");


DelTest3 delTest31 = (name) => { Console.WriteLine(name); };

当你是一个参数的时候可以去掉()  但是你没有参数列表的时候需要加上()
DelTest3 delTest32 = name => { Console.WriteLine(name); };
delTest31("有参数 没有返回值");

1.6Action/Func创建委托

Action委托本身不包含返回值,主要封装那些没有返回结果的函数。Func委托则可用于需要返回值的情况。

//00、自己定义的泛型委托
DelTest4<int> delTest4 = n1 => { Console.WriteLine(n1); };
delTest4(230);
泛型委托
public delegate void DelTest4<T>(T t);

//01、 Action
//Action就是系统帮我们定义好的一个没有参数没有返回值的委托
//委托 没有参数没有返回值 直接使用 不需要再去定义一个委托 
Action action = () => { Console.WriteLine("a"); };
//调用内置委托和调用普通委托一样
action();


//TResult 代表返回值的类型
//第一个int 表示参数一为int类型
//第二个int 表示参数二为int类型
//第三个int 表示参数三为int类型
//第四个string 表示参数四为返回值为string
//最后一个永远是返回值
Func<int, int, int,string> func = (n1, n2,n3) => { return "张三的成绩为"+n1+n2+n3; };
Console.WriteLine(func(100, 200,300)); 

1.7多播委托

多播委托时为了让同一个委托对象,一行调用多个方法同时执行,如游戏中,一个人物按下开火键(委托对象),手中的冲锋枪和步枪同时开火,如果不使用多播委托,需要先进行武器切换选择其中一个进行开火。



Action action = M1;
//如果使用 多播委托 必须在调用之前进行绑定

//多播委托和字符串拼接类似 每拼接一次 在实际的内存中 都会创建一个新的委托对象
//我们看到的是一个委托对象指向了多个方法
//实际上每次拼接后 创建出来新的对象结果

action += M2;
action += M3;
action += M4;
action += M5;

//从结果来看 多播委托的调用是顺序的 ,但是不代表多播委托 的调用就是顺序的 微软随时可以改变顺序

//使用队列

action();



static void M1()
{
    Console.WriteLine("我是第1个方法");
}
static void M2()
{
    Console.WriteLine("我是第2个方法");
}
static void M3()
{
    Console.WriteLine("我是第3个方法");
}
static void M4()
{
    Console.WriteLine("我是第4个方法");
}
static void M5()
{
    Console.WriteLine("我是第5个方法");
}

1.8委托注意事项

委托不能为空,为空的委托不能调用,报错;
在这里插入图片描述
空委托不能调用,所以使用委托前要进行判断:

if(attack!=null)
{
	attack();//调用委托
}

上面的方式过于繁琐,可以简化:

//无参数
attack?.Invoke();
//有参数
attack?.Invoke(a,b);

二、事件

1.概念

C#中的事件是一种特别的委托,它可以被多个方法订阅,并且在特定情况下触发这些方法的执行。

2.使用

2.1事件的定义

  1. 声明事件:在C#中,事件通常通过event关键字声明。它定义了一个可以由多个方法订阅的特殊类型的委托。例如,声明一个没有返回值且不带参数的事件:public event Action MyEvent;。
  2. 委托类型:事件的背后是一个委托类型,它定义了订阅事件的方法的签名。例如,使用内置的Action委托或自定义委托来定义事件。
  3. 访问修饰符:事件的声明可以包含访问修饰符,如public或private,以控制对事件的访问级别。

2.2事件的订阅

  1. 订阅事件:订阅事件是通过+=运算符完成的。例如,myObject.MyEvent += MyMethod;,其中MyMethod是响应事件的方法。
  2. 触发事件:当特定条件满足时,事件会被触发。这通常是在类的内部,通过EventName?.Invoke()形式调用。例如,MyEvent?.Invoke();会触发所有订阅了MyEvent的方法。
  3. 取消订阅:使用-=运算符可以取消事件订阅,如myObject.MyEvent -= MyMethod;

3.案例

委托会遇到的问题
//问题1:我们通过委托来实现事件 由于委托在定义的时候,一般都定义成public 这样就导致了 用户端 不给委托赋值的情况了,也可以直接调用
//问题2:由于委托本质上就是一个数据类型 所以如果赋值为null的话 就会把之前的值覆盖掉 变成一个null对象

//01、直接调用没赋值的委托,可以调用,但是这是不正确的
musicPlayer.BeforePlayMusic();
musicPlayer.AfterPlayMusic();
//02、直接调用事件
//外界不允许直接调用事件  因为事件的语法规定了这一点
musicPlayer.BeforePlayMusic();
musicPlayer.AfterPlayMusic();

//通过音乐播放器  讲解事件在程序中是啥

//实例化音乐播放器类
MusicPlayer musicPlayer = new MusicPlayer();
musicPlayer.BeforePlayMusic += () => { Console.WriteLine("加载我们的歌词"); };
musicPlayer.AfterPlayMusic += () => { Console.WriteLine("跳转下一曲"); };

musicPlayer.BeforePlayMusic += null;
musicPlayer.AfterPlayMusic += null;

//用户单击了播放按钮
musicPlayer.StartMusic();
musicPlayer.EndMusic();

//通俗的说:就是发生【某个变化】的是时候 触发某段代码
//没有用到事件---- - 没有发生变化
//需求1: 我希望在播放音乐之前 ,能够加载我们的歌词
//需求2:我希望播放音乐结束后 能够自动跳转下一曲
//需求应该交给用户 :委托
class MusicPlayer
{
	//01、声明两个事件
    public event Action BeforePlayMusic;
    public event Action AfterPlayMusic;

    //播放音乐  不能给别人调用
    private void MusicPlay()
    {
        //判断用户是否是会员
        //判断用户播放的音乐是否是会员音乐
        //去数据量里面加载音乐文件
        //开始播放.....

        //加载我们的歌词
        BeforePlayMusic();
        Console.WriteLine("音乐播放中");
    }

    //点击按钮播放音乐
    public void StartMusic()
    {
        this.MusicPlay();
        Thread.Sleep(2000);
    }

    public void EndMusic()
    {
        Console.WriteLine("音乐关闭中");
        AfterPlayMusic();
        //跳转下一曲
    }

}

三、委托与事件区别

  1. 事件只能在类的内部进行触发,不能在类的外部进行触发。而委托在类的内部和外部都可触发;
  2. 事件是一个特殊的委托,查看反编译工具之后的代码,发现事件是一个 private 委托
    委托测试:
//03、触发类内部定义的委托
Test te = new Test();
te.SayHello();
//04在类外部进行委托对象赋值并处罚
Test.Foo fo= () => { Console.WriteLine("02、类外部触发的委托!"); };
fo();

//创建类
class Test
{
	//01定义委托	
    public delegate void Foo();
    
    public void SayHello()
    {
    //02在类内部对委托对象进行赋值
        Foo fo=()=> { Console.WriteLine("01、类内部触发的委托!"); };
        fo();
    }

}

在这里插入图片描述
事件测试:

//03实例化类并触发事件
Test te = new Test();
te.SayHello();

class Test
{
//01、定义事件
    public event Foo  Foo1;
    public void SayHello()
    {
    //02、方法绑定
        Foo1+= () => { Console.WriteLine("类内部触发的事件!"); };
        Foo1();
    }

}
//02类外部进行绑定方法,并触发
te.Foo1 += () => { Console.WriteLine("a2"); };
te.Foo1();

class Test
{
	//01定义事件
    public event Foo  Foo1;
}

报错,事件无法在类的外部进行触发
在这里插入图片描述

相关推荐

  1. C#委托事件

    2024-07-09 20:06:07       33 阅读
  2. C# 委托事件

    2024-07-09 20:06:07       38 阅读
  3. C# - 委托事件

    2024-07-09 20:06:07       22 阅读
  4. C# 委托事件 深入

    2024-07-09 20:06:07       28 阅读
  5. 详解C#委托事件

    2024-07-09 20:06:07       27 阅读
  6. 二、C#基础语法( 委托事件

    2024-07-09 20:06:07       50 阅读
  7. C# 系统学习(事件委托

    2024-07-09 20:06:07       32 阅读
  8. C# 委托事件 终章

    2024-07-09 20:06:07       32 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-09 20:06:07       53 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-09 20:06:07       56 阅读
  3. 在Django里面运行非项目文件

    2024-07-09 20:06:07       46 阅读
  4. Python语言-面向对象

    2024-07-09 20:06:07       57 阅读

热门阅读

  1. npm证书过期问题

    2024-07-09 20:06:07       20 阅读
  2. GitHub使用教程(小白版)

    2024-07-09 20:06:07       16 阅读
  3. WebXR:Web上的虚拟与增强现实技术

    2024-07-09 20:06:07       28 阅读
  4. 如何在MATLAB中导入表格数据并进行运算?

    2024-07-09 20:06:07       24 阅读