一、unity里使用委托常见场景
1、在绑定按钮事件的使用场景里,如果想批量地绑定按钮事件,同时想通过绑定事件传递被点击按钮的名字,那么这时候可以通过委托的方式,这样可以将按钮的名字传递给绑定事件处理方法。
btnTake.onClick.AddListener(() =>
{
ClickTakeBtn(go.name);
});
private void ClickTakeBtn(string name)
{
...
}
2、作为事件分发中心。比如网络数据或者外部的消息数据到达了本地后,往往会做一个消息处理器,然后处理器的做法就是把数据做解析,然后转成对应的事件,最后把事件分发出去,转到各个模块。
二、委托的本质
1、定义。C#中的委托是一个类型,它描述了一个方法的签名,即方法的参数类型和返回类型。委托可以看作是一个指向方法的引用,使得我们可以像使用函数指针一样调用这些方法。我们习惯于把数据作为参数传递给方法,所以,给方法传递另一个方法听起来有点奇怪。而有时某个方法执行的操作并不是针对数据进行的,而是要对另一个方法进行操作。更麻烦的是,在编译时我们不知道第二个方法是什么,这个信息只能在运行时得到,所以需要把第二个方法作为参数传递给第一个方法。
2、可以实现几个想要的功能:
- 将一个或多个方法作为参数传递给另一个方法,从而在需要时调用这些方法。
- 实现事件处理程序。
- 实现回调方法。
- 实现异步编程等功能。
3、关于为什么委托是类型安全的。
在C和C++中,只能提取函数的地址,并作为一个参数传递它。C没有类型安全性,可以把任何函数传递给需要函数指针的方法。但是,这种直接方法不仅会导致一些关于类型安全性的问题,而且没有意识到:在进行面向对象编程时,几乎没有方法是孤立存在的,而是在调用方法前通常需要与类实例相关联。所以.NET Framework在语法上不允许使用这种直接方法。如果要传递方法,就必须把方法的细节封装在一种新类型的对象中,即委托。委托只是一种特殊类型的对象,其特殊之处在于,我们以前定义的所有对象都包含数据,而委托包含的只是一个或多个方法的地址。
三、委托的类型
- Delegate 普通委托(类似函数指针)
delegate void IntMethodInvoker(int x); - Action<T> 泛型委托(C#内置委托),void返回类型,有16种不同的参数类型。
- Func<in T,out TResult> (C#内置委托),带返回类型的方法,TResult是返回值。至多也可以传递16个参数类型和一个返回类型。
三、关于委托能进行哪些操作
1、声明一个委托类型。委托声明看上去和C++中方法声明相似,只是没有实现块。
public delegate int MyDelegate(int num);
2、使用该委托类型声明一个委托变量。
3、创建一个委托类型的对象,并把它赋值给委托变量。新的委托对象包含指向某个方法的引用,这个方法的签名和返回类型必须跟第一步中定义的委托类型一致。
MyDelegate myDelegate = new MyDelegate(TestMyDelegate);(同时包含了2、3步)
4、你可以选择为委托对象添加其他方法。这些方法的签名和返回类型必须与第一步中定义的委托类型相同。
myDelegate += TestMyDelegate_B;
5、在代码中你可以像调用方法一样调用委托。在调用委托的时候,其包含的每一个方法都会被执行。
myDelegate(12);
此时TestMyDelegate和TestMyDelegate_B都会调用。
从使用过程可以看出,委托是引用类型,因此有引用和对象。在委托类型声明之后,我们可以声明变量并创建类型的对象。
6、多播委托还能识别运算符“-”和“-=”,以从委托中删除方法的调用。
三、与Lambda关系
从C#3.0开始,就可以使用一种新语法把实现代码赋予委托:Lambda表达式。只要有委托参数类型的地方,就可以使用Lambda表达式。
Lambda传参的方式:
- 一个参数 eg:
Func<string, string> oneParam = s => s.Replace('a', 'b');
Console.WriteLine(oneParam("abc"));
结果:bbc
- 如果委托使用多个参数,就把参数名放在小括号中
Func<int, int, int> twoParam = (i, j) => i * j;
Console.WriteLine(twoParam(2, 4));
结果:8
- Lambda多行代码
Func<int, int, int> test = (i, j) =>
{
i = i + 1;
i = i * j;
return i;
};
Console.WriteLine(test(2, 4));
结果:12
又比如下面的两种写法是等同的:
Func<string, string> oneParam = s => s.Replace('a', 'b');
Func<string, string> oneParam2 = delegate (string s)
{
return s.Replace('a', 'b');
};
第一个string实际是输入参数,第二个参数是返回值,Func是必须包含返回值的。
四、特殊委托(event 修饰)
public delegate int MyDelegate(int num);
public event MyDelegate myDelegate;//这里特意使用了
event来修饰。
光板 delegate对象,可以到处调用它。
有了event只允许+=,而不支持=,并且在其他类中不能直接调用委托了。只能在 “event 修饰的delegate对象” 所属类中使用。
如下,在在 “event 修饰的delegate对象” 所属类中可以使用myDelegate(12);
而在其他类中则不能这么调用,如下图:
C#的事件机制就是从这个特殊委托延伸而来的。
五、委托延伸-事件
GUI编程是事件驱动的,也就是说在程序运行时,它可以在任何时候被时间打断,比如按钮点击、按下按键或系统定时器。在这些情况发生时,程序需要处理事件然后继续做其他事件。
显然,程序事件的异步处理是使用C#事件的绝佳场景。Windows GUI编程如此广泛地使用了事件,以至于对于事件的使用,.NET框架提供了一个标准模式。该标准模式的基础就是System命名空间中声明的EventHandler委托类型。EventHandler委托类型的声明如下:
public delegate void Eventhandler(object sender, EventArgs e);
六、标准事件:EventHandler
class Incrementer
{
public event EventHandler CountedADozen;
//event:关键字
//EventHandler:委托类型
//CountedADozen:事件名
}
定义
从上面代码可以看出:
1、事件是声明在一个类中的,和方法、属性一样,事件是类或结构的成员。
2、事件成员被隐式自动初始化为null。
3、事件是C#语言中一种特殊的委托,是用于实现对象之间的通信的机制。
要求
- 事件声明在一个类中。
- 它需要委托类型的名称,任何附加到事件(如注册)的处理程序都必须与委托类型的签名和返回类型匹配
- 它声明为public,这样其他类和结构可以在它上面注册事件处理程序。
- 不能使用对象创建表达式(new 表达式)来创建它的对象。
示例代码
class Publisher
{
public event EventHandler SimpleEvent;
public void RaiseTheEvent() { SimpleEvent(this, null); }
}
class Subscriber
{
public void MethodA(object o, EventArgs e) { Console.WriteLine("AAA"); }
public void MethodB(object o, EventArgs e) { Console.WriteLine("BBB"); }
}
class MyProgram
{
static void Main()
{
Publisher p = new Publisher();
Subscriber s = new Subscriber();
p.SimpleEvent += s.MethodA;
p.SimpleEvent += s.MethodB;
p.RaiseTheEvent();
Console.WriteLine("\r\nRemove MethodB");
p.SimpleEvent -= s.MethodB;
p.RaiseTheEvent();
}
}
目的:Publisher持有事件引用,并触发事件,Subscriber只能通过订阅SomeEvent来获得事件通知
其中,事件的参数是可以传递过来给Subscriber的。