水一篇,因为《函数方法》章节已经说了,但那个章节比较长,知识点又多,可能有人会看不到。委托事件是C#中的一个难点,但我觉得,和TS/JS中的函数表达式放在一起时,委托和事件就变得很简单了。
一、从TS的函数表达式说起
TS/JS中函数是一等公民,function是一种类型,定义的具体函数是一个值,所以可以作为方法的参数和返回值进行传递。在C#中,函数不是一种类型,也不是一个值,需要通过委托来实现类似功能。如果从TS的角度来理解委托,委托其实很简单。所以,先从TS/JS的函数表达式说起。
//1、函数表达式的定义=================================================================
//通过类型推断来声明
let sum = function(x:number,y:number):number{
return x + y;
}
//先定义再赋值
let sum1: (x:number,y:number) => number
sum1 = function(x:number,y:number):number{
return x + y;
}
//也可以通过type约束
type Sum = (x:number,y:number) => number
let sum2:Sum = function(x:number,y:number):number{
return x + y;
}
//使用箭头函数来简化定义
let sum = (x: number, y: number): number => x + y;
//2、函数作为参数使用================================================================
// 使用箭头函数定义一个函数,返回值为将参数加 1。函数定义建议使用const
const addOne = (num: number): number => num+1;
// 定义一个函数,func参数的类型是函数类型
function applyFunction(func: (num: number) => number, num: number): number {
return func(num);
}
// 调用 applyFunction 函数,传递 addOne 函数和 5 作为参数
const result = applyFunction(addOne, 5);
console.log(result);
二、C#中的委托和事件
2.1 C#委托的演变
//1、使用最早版本的委托==============================================================
//自定义一个委托类型(类似自定义一个类),可以认为是一个方法的模型
//以此模型创建的实例对象,参数和返回值的类型要保持一致
delegate int DeleSum(int x); //委托类型DeleSum,模型要求:1个int类型参数,返回int类型
public class Program
{
public static void Main(string[] args)
{
//创建一个委托对象,并使用AddOne方法赋值
//类似于TS中的const deleSum = function(int x){return x+1};
//下行代码可以简写为:DeleSum deleSum = AddOne
DeleSum deleSum = new DeleSum(AddOne);
//先简单的用一下,调用一下deleSum
deleSum(1); //结果为2
//复杂点,实现将AddOne方法作为参数传入ApplyFunction
ApplyFunction(deleSum,2); //结果为3
}
public int AddOne(int x)
{
return x + 1;
}
public int ApplyFunction(DeleSum func, int num)
{
return func(num);
}
}
//2、使用匿名函数和箭头函数,将定义方法的环节干掉=====================================
//上面我们创建委托对象时,还需要先定义方法AddOne,再给委托对象赋值,下面开始简化
//第一步简化,匿名函数。实现不需要定义AddOne方法
DeleSum deleSum = delegate (int x){return x+1};
//第二步简化,箭头函数。实现不用delegate关键词
DeleSum deleSum = (int x) => {return x+1};
//第三步简化,再次简化箭头函数
DeleSum deleSum = (int x) => x+1;
//3、使用内置的泛型委托类型,将定义委托类型的环节干掉=================================
//上面我们仍然要自定义一个委托类型,然后再创建委托对象
//C#内置了几个泛型的委托类型,我们不需要自定义委托类型,就可以直接使用
Func<int,int> deleSum = (int x) => x+1;
//内置的泛型委托类型主要有Action<T1,T2...>和Func<T1,T2...TResult>
//Action没有返回值,Func的最后一个泛型参数是返回值,入参可以有任意个(好像是0-16个)
//有了Action和Func,基本上不用自己再定义委托类型
//4、最后对比一下,体会一下C#和TS有什么不一样========================================
//TS
let sum = (int x,int y):int => x + y;
//C#
Func<int,int,int> sum = (int x,int y) => x + y;
var a = (int x, int y) => x + y; //利用类型推断
//5、模仿泛型委托,在TS中实现类似功能,使用起来也很爽=================================
type Action = ()=>void;
type Action<T1> = (t1:T1)=>void;
type Action<T1, T2> = (t1:T1, t2:T2)=>void;
......
type Func<TRusult> = ()=>TRusult;
type Func<T1, TRusult> = (t1:T1)=>TRusult;
type Action<T1, T2, TRusult> = (t1:T1, t2:T2)=>TRusult;
......
import type { Action, Func } from './types';
function demo(func:Func<number,bool>, num:number){
func(num);
}
const f1 = (a:number):bool=>{
a>0 ? true : false;
}
demo(f1,10);
2.2 更加强大的C#委托
经过一系列骚操作,C#委托在形式上,已经无限接近灵活的函数表达式。但本质上,两者还是不一样的东西,而且C#的委托拥有更加强大的功能。
2.2.1 多播委托
//多播委托可以实现多个方法的代理,调用委托变量时,多个方法按赋值的顺序调用
class Program
{
static void Main()
{
Action<string> multicastDelegate = null;
multicastDelegate += PrintMessage1;
multicastDelegate += PrintMessage2;
//执行委托对象multicastDelegate后,两个方法都会被调用
multicastDelegate("Hello!");
}
static void PrintMessage1(string message)
{
Console.WriteLine("From PrintMessage1: " + message);
}
static void PrintMessage2(string message)
{
Console.WriteLine("From PrintMessage2: " + message);
}
}
2.2.2 事件
事件是基于委托实现的,利用多播委托的特性,在委托的基础上,又抽象的一层。
//1、委托和事件的关系===============================================================
//下例是一个事件定义和触发的简单案例,一开始会感觉和委托没啥区别
//再领会,会发现事件实现了订阅和触发的分离,在此基本上,实现了GUI的事件机制
//比如下例中的EventClass,你可以认为它是一个Button,定义了点击事件
//在Program中,创建了Button对象,然后订阅事件回调,最后触发事件
//1.1 定义委托类型----------------------------------------
delegate void MyDelegate(int num);
//1.2 定义包含事件的类------------------------------------
class EventClass
{
//事件是类的成员,使用event修饰词,类型必须是委托类型
public event MyDelegate MyEvent;
public void TriggerEvent(int num)
{
MyEvent?.Invoke(num); //定义触发事件的方法
}
}
//1.3 订阅事件和触发事件-----------------------------------
class Program
{
static void Main()
{
EventClass eventClass = new EventClass();//实例化包含事件的类
eventClass.MyEvent += EventHandler1; //订阅事件回调1
eventClass.MyEvent += EventHandler2; //订阅事件回调2
eventClass.TriggerEvent(10); //触发事件
}
//事件回调,触发/发布事件时,执行的业务逻辑。还需要订阅绑定才能生效。
static void EventHandler1(int num)
{
Console.WriteLine("通过事件调用 EventHandler1: " + num);
}
static void EventHandler2(int num)
{
Console.WriteLine("通过事件调用 EventHandler2: " + num);
}
}
//2、实际使用过程中使用框架内置的委托类型============================================
//2.1 EventHandler--------------------------------
//EnentHander:public delegate void EventHandler(object sender, EventArgs e);
class Program
{
static void Main()
{
MyEventSource eventSource = new MyEventSource();
eventSource.SomeEvent += EventSource_SomeEvent;
eventSource.TriggerEvent();
}
static void EventSource_SomeEvent(object sender, EventArgs e)
{
Console.WriteLine("SomeEvent 发生了!");
}
}
//非泛型事件类
class MyEventSource
{
public event EventHandler SomeEvent;
public void TriggerEvent()
{
SomeEvent?.Invoke(this, EventArgs.Empty);
}
}
//2.2 EventHandler<TEventArgs>-----------------------
//EventHandler只能使用预定义参数类型EventArgs,泛型版本可以自定义参数类型
/*public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e)
where TEventArgs : EventArgs;
*/
class Program
{
static void Main()
{
MyEventWithArgs eventSource = new MyEventWithArgs();
eventSource.SomeEventWithArgs += EventSource_SomeEventWithArgs;
eventSource.TriggerEventWithArgs(42);
}
static void EventSource_SomeEventWithArgs(object sender, MyEventArgs e)
{
Console.WriteLine($"SomeEventWithArgs 发生了,参数值: {e.Value}");
}
}
//泛型事件类
class MyEventWithArgs
{
public event EventHandler<MyEventArgs> SomeEventWithArgs;
public void TriggerEventWithArgs(int value)
{
SomeEventWithArgs?.Invoke(this, new MyEventArgs(value));
}
}
//自定义类型参数
class MyEventArgs : EventArgs
{
public int Value { get; set; }
public MyEventArgs(int value)
{
this.Value = value;
}
}
标签:委托,C#,void,TS,number,int,num,第壹章,public
From: https://blog.csdn.net/2401_85195613/article/details/139635205