C#自学笔记:委托与事件
委托
委托代表对一个或多个方法的引用,类似于函数指针,可以将方法作为参数传递给其他方法。委托是函数(方法)的容器,用来存储、传递函数(方法)
本质
委托的本质是一个类,用来定义函数(方法)的类型(返回值和参数的类型)
不同的函数(方法)必须对应和各自“格式”一致的委托
基本语法
关键字:delegate
语法:访问修饰符 delegate 返回值 委托名(参数列表);
写在哪里?
• 可以声明在namespace和class语句块中,但是更多的写在namespace中
简单记忆委托语法就是函数声明语法前面加一个delegate关键字
使用
委托常用在:- 作为类的成员
- 作为函数的参数
• 访问修饰符默认不写,为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);
系统定义好的委托
一般情况下我们不会自己去声明委托,都是使用系统定义好的- Action
using System
//Action其实就是系统定义好的一个无参无返回值的委托
Action action = Fun;
action += Fun;
action();
Action底层
namespace System
{
...public delegate void Action();
}
- 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>();
}
- 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);
}
- 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 委托类型 事件名;
- 事件在类中作为成员变量
- 使用方式和委托一模一样
事件和委托的区别:
- 不能在类外部赋值
- 不能在类外部调用
- 事件只能在外部通过+-=来添加和移除函数
注意:它只能作为成员变量而存在,于类和接口以及结构体中,不能在函数中作为临时变量来使用
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()
{
}
}
使用事件的原因
- 防止外部随意修改置空委托
- 防止外部随意调用委托
- 事件相当于对委托进行了一次封装,让其更加安全
事件和委托的区别
事件就是特殊的委托- 事件不能在外部使用赋值符号=,只能使用+-=运算符,而委托能用
- 事件不能在外部执行,委托可以
- 事件不能作为函数中的临时变量,委托可以。

浙公网安备 33010602011771号