第壹章第14节 C#和TS语言对比-委托事件(仅C#)

发布于:2024-06-19 ⋅ 阅读:(130) ⋅ 点赞:(0)

水一篇,因为《函数方法》章节已经说了,但那个章节比较长,知识点又多,可能有人会看不到。委托事件是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;
    }
}


网站公告

今日签到

点亮在社区的每一天
去签到