C#自学笔记:委托与事件

委托

委托代表对一个或多个方法的引用,类似于函数指针,可以将方法作为参数传递给其他方法。

委托是函数(方法)的容器,用来存储、传递函数(方法)

本质

委托的本质是一个类,用来定义函数(方法)的类型(返回值和参数的类型)

不同的函数(方法)必须对应和各自“格式”一致的委托

基本语法

关键字:delegate

语法:访问修饰符 delegate 返回值 委托名(参数列表);

写在哪里?

• 可以声明在namespace和class语句块中,但是更多的写在namespace中

简单记忆委托语法就是函数声明语法前面加一个delegate关键字

使用

委托常用在:
  1. 作为类的成员
  2. 作为函数的参数
    • 访问修饰符默认不写,为public,在别的命名空间中也能使用
    • 如果设置成private,其他的命名空间就不能用了
基本使用
//声明了一个可以用来存储无参无返回值函数的容器
//这里只是定义了规则,并没有使用
delegate void MyFunCallback();
//委托规则的声明是不能重名的(同一语句快中),也就是说不能重载
//表示用来装载或传递,返回值为int,有一个int参数的函数的委托容器规则
public delegate int MyFun2Callback(int a);

class Program
{
    static void Main(string[] args)
    {
        Cosole.WriteLine("委托");
        //无返回值无参数
        //方式一
        MyFunCallback f1 = new MyFunCallback(Fun);
        //调用委托,会调用里面的所有函数
        f1.Invoke();

        //方式二
        //直接写函数名,不能加括号
        MyFunCallback f12 = Fun;
        f12();

        //有返回值有参数
        //方式一
        MyFun2Callback f2 = new MyFun2Callback(Fun2);
        Console.WriteLine(f2.Invoke(2));
        //方式二
        MyFun2Callback f22 = Fun2;
        Console.WriteLine(f3(1));
    }

    static void Fun()
    {
        Console.WriteLine("123123");
    }

    static int Fun2(int value)
    {
        return value;
    }
}
一般使用
class Text
{
    public MyFunCallback fun;
    public MyFun2Callback fun2;

    public void TestFun(MyFunCallback fun, MyFun2Callback fun2)
    {
        //先处理一些别的游戏逻辑,当这些逻辑处理完了之后再执行传入的函数
        int i = 1;
        i *= 2;
        i += 2;
        //可以直接执行
        fun();
        fun2(i);
        //也可以先存起来,以后再执行
        this.fun = fun;
        this.fun2 = fun2;
    }

    //增
    public void AddFun(MyFunCallback fun, MyFun2Callback fun2)
    {
        this.fun += fun;
        this.fun2 += fun2;
    }

    //删
    //要是为空的话也能减,就是说如果没有这个函数也能减,不会报错
    public void RemoveFun(MyFunCallback fun, MyFun2Callback fun2)
    {
        this.fun -= fun;
        this.fun2 -= fun2;
    }
}

多播委托

委托变量可以存储多个函数

按顺序依次执行添加的函数

//如何用委托存储多个函数
MyFunCallback ff = Fun;
ff += Fun;
ff();

MyFunCallback ff = null;
ff + Fun;
ff += Fun;
ff();
//从容器中移除指定的函数
ff -= Fun;
//多减不会报错,无非就是不处理而已
ff -= Fun;
ff();
//清空容器
ff = null;

ff();	//为空时运行会报错
//规范写法
if (ff != null)
{
    ff();
}
//简写
ff?.Invoke();

Test t = new Test();
t.AddFun(Fun, Fun2);
t.fun();
t.fun2(50);


注意,如果存储了多次同一个函数,会多次调用同一个函数

泛型委托

委托是支持泛型的,可以让返回值和参数可变,更方便我们使用
delegate T MyFun3Callback<T, K>(T v, K k);

系统定义好的委托

一般情况下我们不会自己去声明委托,都是使用系统定义好的
  1. Action
using System

//Action其实就是系统定义好的一个无参无返回值的委托
Action action = Fun;
action += Fun;
action();

Action底层

namespace System
{
    ...public delegate void Action();
}
  1. Func<>

是一个泛型委托

//可以指定返回值类型的泛型委托
Func<string> funcString = Fun4;
Func<int> funcInt = Fun5;

static string Fun4()
{
    return "";
}

static int Fun5()
{
    return 1;
}

Func底层

namespace System
{
    ...public delegate TResult Func<out TResult>();
}
  1. Action<>

可以传n个参数的委托,最多传16个参数,这种不算重载,泛型不同代表着命名不同,说明有16个不同的委托

//有参无返回值
Action<int, string> action2 = Fun6;

static void Fun6(int i, string s)
{
}

Action<>底层

namespace System
{
    ...public delegate void Action<in T>(T obj);
}
  1. Func<>

可以传n个参数的并且有返回值的,系统也提供了16个委托

注意⚠️:参数比Action多一个,因为有一个参数是返回值,放在最后一个位置,所以一共有17个委托,要是没有参数的话,就只有一个返回值作为参数

Func<int, int> func = Fun2;

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

Func<>底层

namespace System
{
    ...public delegate TResult Func<in T, out TResult>(T arg);
}

事件

事件是基于委托的存在

事件是委托的安全包裹,让委托的使用更具有安全性

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

使用

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

  1. 事件在类中作为成员变量
  2. 使用方式和委托一模一样

事件和委托的区别:

  1. 不能在类外部赋值
  2. 不能在类外部调用
  3. 事件只能在外部通过+-=来添加和移除函数

注意:它只能作为成员变量而存在,于类和接口以及结构体中,不能在函数中作为临时变量来使用

class Test
{
    //委托成员变量 用于存储 函数的
    public Action myFunCallback;
    //事件成员变量 用于存储 函数的
    public event Action myEvent;

    public Test()
    {
        //事件的使用和委托一模一样,只是有些细微的区别
        myFunCallback = TestFun;
        myFunCallback += TestFun;
        myFunCallback -= TestFun;
        myFunCallback();
        myFunCallback.Invoke();
        myFunCallback = null;
        
        myEvent = TestFun;
        myEvent += TestFun;
        myEvent -= TestFun;
        myEvent();
        myEvent.Invoke();
        myEvent = null;
    }

    public void TestFun()
    {
    }

    public void DoEvent()
    {
        myEvent?.Invoke();
    }
}

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("事件");

        Test t = new Test();
        //委托可以在外部赋值
        t.myFunCallback = null;
        t.myFunCallback = ProgramFun;
        //事件不能在外部赋值,会报错
        t.myEvent = null;
        t.myEvent = ProgramFun;
        //虽然不能直接赋值,但是可以通过加减去添加或移除函数
        t.myEvent += ProgramFun;
        t.myEvent -= ProgramFun;
        //⚠️但是这样写是错误的,因为事件里只重载了+=这种运算符
        t.myEvent = t.myEvent + ProgramFun;

        //委托是可以在外部调用的
        t.myFunCallback();
        t.myFunCallback.Invoke();
        //事件不能在外部调用,会报错
        t.myEvent();
        t.myEvent.Invoke();
        //只能在类的内部封装和调用
        t.DoEvent();
    }

    static void ProgramFun()
    {
    }
}

使用事件的原因

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

事件和委托的区别

事件就是特殊的委托
  1. 事件不能在外部使用赋值符号=,只能使用+-=运算符,而委托能用
  2. 事件不能在外部执行,委托可以
  3. 事件不能作为函数中的临时变量,委托可以。
posted @ 2025-07-29 18:36  柠凉w  阅读(31)  评论(0)    收藏  举报