【从零开始入门unity游戏开发之——C#篇33】C#委托(`Delegate`)和事件(`event` )、事件与委托的区别、Invoke()的解释
文章目录
- 一、委托(`Delegate`)
- 1、什么是委托?
- 2、委托的基本语法
- 3、定义自定义委托
- 4、如何使用自定义委托
- 5、多播委托
- 6、C# 中的系统委托
- 7、`GetInvocationList` 获取多个函数返回值
- 8、总结
- 二、事件(`event` )
- 1、事件是什么?
- 1.1. **事件是基于委托的存在**:
- 1.2. **事件是委托的安全包裹**:
- 1.3. **事件是一种特殊的变量类型**:
- 2、事件的使用
- 2.1 **声明事件语法**:
- 2.2 **事件的使用**:
- 2.3 **事件的触发**:
- 3、**委托与事件的区别**:
- 4、为什么要使用事件?
- 4.1. **防止外部随意置空委托**:
- 4.2. **防止外部随意调用委托**:
- 4.3. **事件封装了委托,增强了安全性**:
- 5、事件与委托的区别总结
- 5.1. **赋值操作**:
- 5.2. **调用方式**:
- 5.3. **临时变量**:
- 5.4. **可见性和封装**:
- 6、总结
- 三、补充`Invoke()`的解释说明
- 专栏推荐
- 完结
一、委托(Delegate
)
1、什么是委托?
委托(Delegate
)是 C# 中的一个重要概念,它可以被理解为“函数(方法)的容器
”。更具体地说,委托是用来存储、传递函数(方法)的类型,允许我们将方法作为参数传递给其他方法或存储在变量中。委托的本质是一个类,用来定义方法的类型(即方法的返回值和参数类型)。
- 委托本质上是一个类型,它定义了方法的类型:返回值类型和参数类型。
- 委托用于
存储方法
,它帮助你把方法传递给其他方法,或者在程序中动态调用。 - 委托是一个
引用类型
2、委托的基本语法
-
关键字:
delegate
-
语法:
[访问修饰符] delegate 返回值类型 委托名(参数列表);
示例:
delegate void MyDelegate(); // 无返回值,无参数的委托 delegate int MyDelegateWithParam(int x); // 返回 int,带一个参数的委托
- 委托可以声明在类(
class
)或命名空间(namespace
)中,一般情况下会声明在命名空间中。 - 委托声明的访问修饰符可以是
public
(默认是public
),也可以是private
,protected
,internal
等。
3、定义自定义委托
-
无参无返回值的委托
public delegate void MyFun();
-
带参数和返回值的委托
public delegate int MyFun2(int a);
-
泛型委托:
委托是支持泛型的,可以让返回值和参数的类型灵活变更。delegate T MyFun3<T, K>(T v, K k);
4、如何使用自定义委托
委托变量相当于一个函数的容器,可以存储函数,并通过委托来调用它。
-
基本用法
- 通过
new
关键字创建委托实例,并调用它。
public delegate void MyFun(); static void Fun() { Console.WriteLine("Hello from Fun!"); } static void Main(string[] args) { MyFun f = new MyFun(Fun); // 创建委托实例 f.Invoke(); // 调用委托,输出: Hello from Fun! }
- 通过
-
简化方式(委托隐式转换)
委托可以直接引用方法,而不需要new
操作符。MyFun f2 = Fun; f2(); // 调用 Fun() 方法
-
带参数的委托
如果委托方法需要参数,可以在调用时传递参数。public delegate int MyFun2(int a); static int Fun2(int x) { return x * 2; } static void Main(string[] args) { MyFun2 f3 = Fun2; Console.WriteLine(f3(3)); // 输出: 6 }
-
委托作为参数
委托经常作为方法的参数,用于传递行为。public delegate void MyFun(); // 定义委托类型 class Test() { public void TestFun(MyFun fun) { // 执行其他逻辑 fun(); // 调用传入的委托方法 } } class Program { static void FunLog() { Console.WriteLine("Hello from Fun!"); } public static void Main() { MyFun Fun = FunLog; // 创建委托实例 Test t = new Test(); t.TestFun(Fun); // 传递委托 } }
结果打印
5、多播委托
委托可以存储多个方法(多播委托)。通过多播委托,可以在一个委托实例中注册多个方法,调用时按顺序执行这些方法。
-
添加多个方法到委托
MyFun f = Fun; f += Fun2; f(); // 调用 Fun 和 Fun2 方法
-
移除方法
f -= Fun; f(); // 只调用 Fun2 方法
-
清空所有注册的方法
f = null;
6、C# 中的系统委托
C# 提供了多个预定义的委托类型,主要有 Action
和 Func
。这些委托可以大大简化我们的编程工作。
-
Action 委托
Action
委托用于没有返回值
的情况。它可以有0到16个参数。Action委托的语法结构
Action<参数类型1, 参数类型2, ..., 参数类型N>
-
无参数:
Action action = Fun; action += Fun2; action();// 调用 Fun 和 Fun2 方法
-
带参数:
Action<int, string> action2 = Fun2; action2(1, "Hello");
-
-
Func 委托
Func
委托用于有返回值
的情况。它也支持0到16个参数。
Func
委托的最后一个类型参数
总是方法的返回值类型
,而其他的参数类型则是方法的输入参数类型。Func 委托的语法结构
Func<参数类型1, 参数类型2, ..., 参数类型N, 返回值类型>
-
无参数,有返回值:
static int FunLog() { return 1; } public static void Main() { Func<int> func = FunLog; int result = func(); Console.WriteLine(result);// 打印1 }
-
带参数,有返回值:
static int FunLog(string str) { return int.Parse(str); } public static void Main() { Func<string, int> func = FunLog; int result = func("111"); Console.WriteLine(result);// 打印111 }
-
7、GetInvocationList
获取多个函数返回值
当用有返回值的委托容器存储多个函数时
using System;
class Program
{
static void Main()
{
// 创建一个 Func 委托,返回 string 类型
Func<string> funTest = () =>
{
Console.WriteLine("第一个函数");
return "1";
};
// 将更多函数添加到委托链
funTest += () =>
{
Console.WriteLine("第二个函数");
return "2";
};
funTest += () =>
{
Console.WriteLine("第三个函数");
return "3";
};
// 如果直接调用委托,会执行所有函数逻辑,但是只能获取到最后一个返回值
Console.WriteLine("直接调用返回值:");
Console.WriteLine(funTest()); // 只会输出 "3"
}
}
输出结果:
直接调用返回值:
第一个函数
第二个函数
第三个函数
3
如果想要获取每个函数的返回值,可以通过 GetInvocationList 方法
foreach (Func<string> del in funTest.GetInvocationList())
{
Console.WriteLine(del()); // 输出每个函数的返回值
}
输出结果:
第一个函数
1
第二个函数
2
第三个函数
3
- funTest.GetInvocationList() 返回一个委托数组,数组中的每个元素都是一个 Func 委托实例,表示容器中每个方法。
- 我们通过 foreach 遍历这些委托实例,并单独执行每个方法,获取它们的返回值。
8、总结
- 委托 是 C# 中用来装载方法的类型,允许将方法作为参数传递或者存储。
- 委托的定义语法包括
delegate
关键字,后接返回类型、委托名称以及参数列表。 - 委托支持泛型,可以创建灵活的、可重用的函数容器。
- 委托可以作为方法的参数,或作为类的成员变量,用于实现事件、回调等功能。
- C# 提供了 没有返回值
Action
和 有返回值Func
等预定义的委托类型,可以减少开发者手动定义委托的工作量。
二、事件(event
)
在 C# 中,事件是基于委托的,它对委托进行了封装,提供了一些安全性和约束。事件是 C# 中一种特殊的成员,通常用于发布-订阅模式,允许一个对象(发布者)通知其他对象(订阅者)某些事情的发生。
1、事件是什么?
1.1. 事件是基于委托的存在:
- 委托可以理解为对方法的引用,而事件是基于委托的,委托在事件中扮演着核心角色。
1.2. 事件是委托的安全包裹:
- 事件通过封装委托,限制了委托的使用,使其更具安全性。特别是防止外部代码随意更改委托(赋值和调用)。
1.3. 事件是一种特殊的变量类型:
- 在 C# 中,事件是一种特殊的变量,它用于存储委托类型的引用,但它具有一些额外的限制。
2、事件的使用
2.1 声明事件语法:
事件的声明语法如下:
[访问修饰符] event 委托类型 事件名;
例如:
public event Action MyEvent;
2.2 事件的使用:
- 事件作为类的成员变量,类似于委托。
- 事件和委托的使用方法非常相似,主要的区别在于事件增加了访问限制。
示例:
public class Test
{
public Action MyFun; // 委托类型成员变量
public event Action MyEvent; // 事件类型成员变量
}
2.3 事件的触发:
- 事件的触发只能在事件的拥有者(类)内部进行,通过调用事件处理方法。
- 一般来说,可以通过一个方法来触发事件,例如:
public void DoEvent()
{
if (MyEvent != null)
{
MyEvent.Invoke(); // 安全地触发事件
}
}
3、委托与事件的区别:
- 事件不能在类外部赋值:委托可以在类外部进行赋值(例如:
myFun = TestFun
),但事件只能通过+=
和-=
操作符来添加和移除事件处理器。 - 事件不能在类外部调用:委托可以在类外部直接调用(例如:
myFun()
),而事件只能在类内部触发(通常使用事件发布方法)。
示例:
public void TestFun()
{
Console.WriteLine("123");
}
// 委托使用
myFun += TestFun;
myFun(); // 调用委托
// 事件使用
myEvent += TestFun; // 添加事件处理器
myEvent(); // 错误,无法在类外部调用事件
4、为什么要使用事件?
4.1. 防止外部随意置空委托:
- 事件的最大优势之一是它防止了外部代码直接修改委托的值(置空)。这保证了事件的安全性,防止委托被外部代码随意设置为
null
或重新赋值,避免了潜在的错误。
4.2. 防止外部随意调用委托:
- 事件提供了对委托的封装,确保事件只能通过
+=
或-=
操作符来添加和移除事件处理器,不能通过外部代码直接调用。
4.3. 事件封装了委托,增强了安全性:
- 事件是一种委托的封装,提供了更高的安全性和可控性,使得事件处理过程更加规范。外部代码无法直接修改事件的内容,只能通过规范的方式来订阅和退订事件。
5、事件与委托的区别总结
5.1. 赋值操作:
- 委托:可以在外部赋值(
=
)。 - 事件:不能直接赋值,只能通过
+=
和-=
操作符来添加和移除事件处理器。
5.2. 调用方式:
- 委托:可以在外部直接调用(
myFun()
)。 - 事件:只能在类内部通过触发方法调用(
MyEvent.Invoke()
)。
5.3. 临时变量:
- 委托:可以作为局部变量在方法中使用。
- 事件:不能作为临时变量,只能作为类的成员存在。
5.4. 可见性和封装:
- 委托:没有访问控制的封装机制,外部可以自由操作。
- 事件:有封装,提供了更严格的访问控制,确保安全性。
6、总结
- 事件本质上是一个委托的封装,提供了额外的安全性。它限制了委托在外部的赋值和调用,使得事件的触发只能在拥有事件的类内部进行。
- 事件的主要用途是实现发布-订阅模式,当某个对象发生变化时,能够通知其他对象,避免外部代码直接修改和调用事件。
- 事件与委托的主要区别在于访问权限的控制,事件通过严格的封装和访问控制来增强安全性,避免外部随意操作。
三、补充Invoke()
的解释说明
Invoke() 是委托
和事件
中常用的方法,它的作用是调用委托所指向的方法。当你在委托或者事件上调用 Invoke() 时,它会执行该委托或事件注册的所有方法。
示例:
public delegate void MyDelegate(string message);
public class Program
{
static void Main()
{
MyDelegate myDelegate = new MyDelegate(PrintMessage);
myDelegate.Invoke("Hello, World!"); // 调用委托的 Invoke 方法
}
static void PrintMessage(string message)
{
Console.WriteLine(message);
}
}
Invoke()
是隐式调用的,因此你也可以直接使用 myDelegate()
来调用它。这两者的作用是相同的。实际上,Invoke()
是自动由 C# 编译器生成的,如果你使用 myDelegate()
,编译器会在幕后调用 Invoke()
。
对于事件同理,Invoke()
方法用于触发事件并调用所有注册的事件处理程序。事件本质上是封装了委托,因此通过 Invoke()
可以调用所有订阅该事件的方法。
专栏推荐
地址 |
---|
【从零开始入门unity游戏开发之——C#篇】 |
【从零开始入门unity游戏开发之——unity篇】 |
【制作100个Unity游戏】 |
【推荐100个unity插件】 |
【实现100个unity特效】 |
【unity框架开发】 |
完结
赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注
,你的每一次支持
都是我不断创作的最大动力。当然如果你发现了文章中存在错误
或者有更好的解决方法
,也欢迎评论私信告诉我哦!
好了,我是向宇
,https://xiangyu.blog.csdn.net
一位在小公司默默奋斗的开发者,闲暇之余,边学习边记录分享,站在巨人的肩膀上,通过学习前辈们的经验总是会给我很多帮助和启发!如果你遇到任何问题,也欢迎你评论私信或者加群找我, 虽然有些问题我也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~