委托
委托代表对一个或多个方法的引用,类似于函数指针,可以将方法作为参数传递给其他方法。委托是函数(方法)的容器,用来存储、传递函数(方法)
本质
委托的本质是一个类,用来定义函数(方法)的类型(返回值和参数的类型)
不同的函数(方法)必须对应和各自“格式”一致的委托
基本语法
关键字: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(){}
}
使用事件的原因
- 防止外部随意修改置空委托
- 防止外部随意调用委托
- 事件相当于对委托进行了一次封装,让其更加安全
事件和委托的区别
事件就是特殊的委托- 事件不能在外部使用赋值符号=,只能使用+-=运算符,而委托能用
- 事件不能在外部执行,委托可以
- 事件不能作为函数中的临时变量,委托可以。