企业网站建设原则,免费的个人网站平台,深圳雅迅公司网站建设,建湖人才网最新招聘信息文章目录 委托委托的定义委托实例化委托的调用多播委托 为什么使用委托#xff1f;官方委托泛型方法和泛型委托 事件为什么要有事件#xff1f;事件和委托的区别#xff1a; 题外话——委托与观察者模式 委托
在 .NET 中委托提供后期绑定机制。 后期绑定意味着调用方在你所… 文章目录 委托委托的定义委托实例化委托的调用多播委托 为什么使用委托官方委托泛型方法和泛型委托 事件为什么要有事件事件和委托的区别 题外话——委托与观察者模式 委托
在 .NET 中委托提供后期绑定机制。 后期绑定意味着调用方在你所创建的算法中至少提供一个方法来实现算法的一部分。
说的更简单一点我们可以通过调用委托实现一大串方法的处理。委托像是一个装函数的容器在我之前的文章里将其比作了服务员点菜时写的小单子当触发委托的时候就是将单子交给后厨厨师就会按顺序做出我们点的菜。 委托的定义
当我们定义委托时需要使用到delegate关键字。 delegate void MyFun();// 委托不可重载delegate void MyFunT();// 带泛型的函数名MyFunT和MyFun不同delegate int MyFun2(int i);委托是不可重载的。无论修改函数返回值定义还是增加参数只要存在相同函数名的委托就无法通过编译。但是带泛型的不是相同函数名例如MyFun,MyFun,MyFunT,K可以同时存在
委托实例化
当想要使用委托的时候需要将其先实例化存在两种实例化方式new一个对象并为其赋值第一个函数 int Input(int i){Debug.Log(i);return i;}void Start()
{MyFun2 myFun new MyFun2(Input);// new的时候需要定义第一个调用函数MyFun myFun1 null;// 委托初始化可为空但是空委托触发时会报错
}需要注意第一委托的初始化必须放在方法中执行不能在定义时初始化第二委托的格式和函数的格式必须完全相同。委托的返回值指定什么类型赋给委托的函数就必须也是相同类型而委托有多少入参函数也要有同样的入参
delegate int MyFun2(int i);
int Input(int i)
{return i;
}
void Input1(int i)
{
}
int Input2(string i)
{return 1;
}
MyFun2 myFun new MyFun2(Input);
myFun Input1; // 返回值不同报错
myFun new MyFun2(Input2); // 入参不同报错委托的调用
当调用委托的时候我们需要确保委托不为空可以使用反射和直接调用的方法来调用委托
myFun1();//委托为空执行会报错
myFun(100);
myFun.Invoke(100); //带有入参的委托需要在调用时给出入参
myFun1.Invoke(); //委托为空执行会报错
myFun1?.Invoke(); //使用?.Invoke(),当委托为空时不调用多播委托
委托可以包含多个方法并触发这样的委托被我们称为多播委托对于委托中的方法可以简单地使用如下语句进行增减
myFun1 Fun;
myFun1 - Fun2;// 当减去方法的时候若委托中没有对应函数
//编译即使委托为空和执行都不会报错
myFun1 SayHi;在多播委托中委托中事件的触发顺序是按照执行语句时我们向委托中添加的方法的顺序来执行的如果先添加A再添加B方法则就是先执行A再执行B。 为什么使用委托
请看下面的一个例子 class Test{public MyFun fun;public MyFun2 fun2;int i10;public void TestFun(MyFun fun,MyFun2 fun2){i i*100;fun2.Invoke(i);fun.Invoke();}}在上面的这个类中我们使用一个函数来接收两个委托随后在函数中处理了参数i并定义了两个委托的调用次序。而最终这个类在实例化后可以在需要时来调度这个函数方法。
当我们需要在类在实例化后处理一系列方法例如实例化了一个Monster类的小怪Ai对应它的伤害fun1fun2对应攻击后会触发的一系列反应。这样一个小怪攻击的方法就简单的完成了。
一方面我们想要调用函数的话首先函数是无法作为参数传入到其他函数中的其次如果调度函数将考虑一大堆问题例如需要调度的函数的访问修饰是不是public是否要先引入其他的类如果要修改这个攻击方式怎么办等等问题。使用委托就不用考虑这些问题只需要保证委托和调用方法的格式是相一致的将委托丢入方法后直接调用把整个问题抽象到只考虑我们应该在什么时候触发委托就行了。 另一方面在我们的结构中触发的是委托而不是函数也就意味着我们可以随意地修改攻击方法触发的函数只需简单地对委托进行方法的加减。
我想介绍的委托的另一个使用例子来自unity官方在官方提供的New InputSystem中每个按键触发的就是委托。这使得我们可以像接口一样很简单地修改按键对应的方法。以往是用if检测按键触发然后调度方法现在我们可以通过委托把该调度的方法直接加入到委托多播上。这样更方便修改也更灵活。 官方委托
虽然我们可以自行定义委托但是毕竟代码是给人看的可能有人接受你的代码后不知道对应的类型是类结构体还是什么东西只有当他查看引用了之后才知道这是一个委托。而官方很贴心的提供了一些有名字的委托
Action action test.Fun; // void Action() 无参无返回委托
Actionstring action1 NewString; // void ActionT() 有参无返回泛型委托,最多接受16个泛型传入
action1 Tstring; // 如果函数同样接收泛型那么函数的泛型会自动接受Action委托声明时给出的对应泛型
Funcint func Input;// T Funcout T(); 无参带返回值泛型委托,使用out修饰代表该委托是协变的,最后一个泛型决定返回值类型
Funcint,string,int func1 Input;// Result FuncT1,T2...Result(T1 arg1, T2 arg2...) 有参带返回值泛型委托,最多接受16个泛型传入官方委托总共有两个Action代表了无返回值委托Func则是带返回值委托。而这两个委托又有同名泛型定义最多接受16个泛型每个泛型对应着一个入参。
使用它们的时候就和正常委托一样举个例子
void Input(){}
int Input()
{return 1;
}
int InputT1,T2(T1 a,T2 b)
{return 1;
}
void TstringT(T i){}
void NewString(string i){}Action action Input; // Action无入参无返回值对应delegate void Action()
Actionstring action1 NewString;// Action有入参无返回值对应void ActionT(T t)
// 上句对应的NewString类型也要完全一致也是无返回值入参一个类型为string
Func func Input;// Func无入参有返回值对应TResult Funcout TResult()out修饰协变
// 注意当使用Func委托的时候至少需要定义一个泛型这个泛型对应的不是入参而是返回值的类型
Funcint,string,int func1 Input;// 同理右侧最后一个泛型int代表了返回值的类型
// Func定义的三个泛型则需要委托的方法要有返回值且有两个入参上述是官方委托的使用方法当不需要返回值的时候使用Aciton当需要返回值的时候使用Func并且还需要定义最后一个泛型来代表返回值的类型。
泛型方法和泛型委托
int InputT1,T2(T1 a,T2 b)
{return 1;
}
Funcint,string,int func1 Input;在上述代码中函数Input定义了两个泛型而委托Func中有三个泛型实际上他们是匹配的毕竟Func中最后一个泛型代表了返回值的类型。
现在有下列定义
T InputT1,T2,T(T1 a,T2 b)
{T t default(T);return t;
}
Funcint,string,int func1 Input; // 报错上述代码看起来很合理三个泛型依次赋值给Input的三个泛型实际上不行。Func只会把前两个泛型定义给函数因此第三个泛型编译器无法推断。 事件
学习了委托事件其实也就学习了事件和委托基本上一模一样
class Test
{delegate void NewDel();event NewDel MyFun; // 注意定义事件时其访问性必须与委托一致
}在定义事件时使用event关键字来修饰委托名并命名出事件委托的定义事件和委托的使用基本一模一样就不再赘述了。唯一的区别在于事件是无法在类的外部进行赋值和调用的 class Test{public delegate void NewDel();public NewDel del null;public event NewDel MyFun;public Test(){del NewFun;MyFun NewFun;}public void NewFun(){}}void Start(){Test t new Test();t.del();t.del.Invoke();t.MyFun(); // 报错t.MyFun.Invoke(); // 报错t.MyFun null; // 报错t.MyFun Appli;t.MyFun - Appli;}void Appli(){}在类的外部不能直接操作Event只能进行加减函数的操作。并且事件也无法作为临时变量在函数中使用智能作为成员存在于类和接口以及结构体中。
为什么要有事件
防止外部随意置空委托防止外部随意调用委托事件相当于对委托进行了一次封装使其更加安全
事件和委托的区别
事件不能在外部赋值在外部只能对其进行函数委托的加减操作事件不能在外部执行而委托在哪都能执行事件不能作为函数中的临时变量而委托可以 题外话——委托与观察者模式
首先简单介绍一下什么是观察者模式观察者模式是对象间的一种一对多的依赖关系当一个对象的状态发生改变时所有依赖于它的对象都得到通知并被自动更新。
举个例子现在有n个外贸公司这些公司都有进口和出口的业务当人民币贬值时人民币贬值导致这些公司更倾向出口业务而人民币升值则更倾向进口业务 class Company{public void Update(bool 贬值了){if (贬值了) { Debug.Log(出口); }else { Debug.Log(进口); }}}class CHY{private ListCompany Companies new ListCompany();bool state false;void 贬值()// 原谅我不懂贬值的英文幸好C#可以起中文名作为举例足够了{state true;Debug.Log(人民币贬值);}void 升值(){state false;Debug.Log(人民币升值);}bool setState(){// 判断升值还是贬值的代码return state;}void getState(){foreach(var company in Companies){company.Update(state);}}}在上述代码中如果人民币发生增值和贬值都会通过getState()来通知那些观察者公司当观察者发现人民币汇率变化则选择相应的战略计划。这就是一个简单的观察者模式。
在观察者模式中明显这些相关联的类具有很强的依赖性被观察者发生变化则会向所有观察者广播通知而观察者则会做出相应的反应。
那么委托和观察者模式有什么关系呢仔细想来其实委托和观察者本质上是相似的他们的处理模式都是 启动——通知——逐一处理
现在让我们把上述观察者事件加入到一个委托当中也就是
Funcbool ChangeState setState;// 代码有点小问题意思到了就行
ChangeState Company1.Update;
ChangeState Company2.Update;
......
ChangeState.Invoke(state);当我们触发ChangeState事件之后人民币状态改变了而委托中附加的公司也通知了。所实现的功能和观察者模式是一样的。那我们为什么要用委托实现呢原因是解耦。
第一个例子中观察者和被观察者的关系是十分紧密的以致于存在依赖需要由被观察者来通知观察者。耦合性过高。而使用委托之后就无需定义被观察者中的getState()方法也无需定义ListCompany只需将观察者的更新状态方法添加到委托中即可。大大降低了两个类的耦合性。使用委托被观察者完全不知道观察者的存在这才是真正的观察者模式。