Unity之C#学习笔记(15):特化委托Action, Func和Lambda表达式

前篇链接:Unity之C#学习笔记(14):委托和事件 Delegates and Events

前一篇中我们已经讲了C#中的委托(不清楚的小伙伴可以点击上面的链接),这节来聊聊两种“特化”的委托:Action和Func。

Action,就是只有参数没有返回值的委托。只有参数意味着函数可以有零个、一个或多个参数,没有返回值,即返回类型为void。Action从字面意义上很好理解,“一个活动”,就是做一件事,做完就行了,不用报告结果。

从一个简单的例子看起:Player脚本当按下空格键时要掉血,同时通知UIManager更新UI,显示当前生命值。使用委托,我们会这样实现:

Player:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player : MonoBehaviour
{
    public delegate void OnDamageReceived(int currentHealth);
    public static event OnDamageReceived onDamageReceived;

    public int health = 100;

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
            ReceiveDamage();
    }

    void ReceiveDamage()
    {
        health--;
        if (onDamageReceived!= null)
            onDamageReceived(health);
    }
}

UIManager:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class UIManager : MonoBehaviour
{
    public Text TxtPlayerHealth;

    void Start()
    {
        Player.onDamageReceived += UpdatePlayerHealth;
    }

    private void UpdatePlayerHealth(int health)
    {
        TxtPlayerHealth.text = "玩家生命值:" + health;
    }
}

使用Action,我们可以简化Player中先声明delegate类型,再创建event变量的过程。

using System; // 使用Action要包含System命名空间
// ...
    public delegate void OnDamageReceived(int currentHealth);
    //public static event OnDamageReceived onDamageReceived;

    public delegate void OnComplete(int currentHealth);
    //public static event OnComplete onComplete;

    public static Action onComplete;            // 零个参数的Action,不打开尖括号
                                                // 效果与以上两行相同
    public static Action<int> onDamageReceived; // 一个或多个参数的Action,打开尖括号,写参数类型列表

其他使用方式与委托相同。根据微软官方文档,至多提供包含16个参数的Action模版。
Unity之C#学习笔记(15):特化委托Action, Func和Lambda表达式_第1张图片
Func与Action相反,是有返回值(即不为void)的委托,也可以有参数。目前为止,我们用的委托返回值都是void类型的。委托当然可以有返回值,但是有一个问题:如果有多个函数注册到一个有返回值的委托,该委托最终保留的是最后一个函数的返回值,前面函数的返回会被丢弃。例如下面的例子:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GameManager : MonoBehaviour
{
    public delegate int GetLength(string text);
    public event GetLength getLength;

    private void Start()
    {
        getLength += SmartFunc;
        getLength += DumbFunc;

        Debug.Log(getLength("Hello"));
    }
    
    int SmartFunc(string text)
    {
        Debug.Log("I'm smart!");
        return text.Length;
    }

    int DumbFunc(string text)
    {
        Debug.Log("I'm dumb...");
        return 0;
    }
}

Unity之C#学习笔记(15):特化委托Action, Func和Lambda表达式_第2张图片
可以看到SmartFunc和DumbFunc都被调用了,但最终保留的是DumbFunc的返回值。所以,有返回值的委托在多播中是用的比较少的。

言归正传,继续说Func的使用。Func一定要打开尖括号(因为至少有一个返回值类型),尖括号中先写参数类型,最后一项写返回值类型。例如改写上面的程序,只需要将声明委托类型+声明变量的两行改为声明Func变量的一行:

    //public delegate int GetLength(string text);
    //public event GetLength getLength;
    
	// 与Action相同,不要忘记using System;
    public Func<string, int> getLength;

根据文档,Func也提供0到16个参数+1个返回值的模版。

最后我们来聊聊Lambda表达式。这个写过前端JS的同学们肯定很熟悉了,就是箭头函数。对于一些只在委托中使用一次的函数,实际上赋予名称—按名称绑定的方式显得有些冗余,最好是有一种方法能将函数体直接写在绑定处。C#从2.0开始支持这一特性,引入了匿名函数的概念,其语法为:

	delegate void TestDelegate(string s);
	TestDelegate testDelA = new TestDelegate(M); 						  // C# 1.0,不支持匿名函数,M为前面定义的函数
	TestDelegate testDelB = delegate(string s) { Console.WriteLine(s); }; // C# 2.0,匿名函数写法

C# 3.0引入了Lambda表达式,其表达性更强,更简洁:

	TestDelegate testDelC = (x) => { Console.WriteLine(x); }; // C# 3.0 Lambda表达式。TestDelegate函数签名确定了x的类型为string

对于前面获取长度的例子,使用Lambda表达式写为:

	getLength = (x) => x.Length;

括号内写参数列表,“=>”右侧写函数体。几种具体情况:

函数体只有一句return xx,称为expression lambda,箭头右侧直接写该返回表达式,不用写return(例如上例):

	(input-parameters) => expression

函数体有多个语句时,称为statement lambda,函数体用大括号包围:

	(input-parameters) => { <sequence-of-statements> }

函数只有一个参数时,括号可以省略:

	getLength = x => x.Length;

函数没有参数时,要写空括号:

	() => ...;

当编译器无法推断参数类型时,要显式声明。所有参数要么全部隐式声明,要么全部显式声明:

	(int x, string s) => s.Length > x;

你可能感兴趣的:(Unity)