C#委托类型

2021/7/15 11:05:49

本文主要是介绍C#委托类型,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

目录

      • 委托的重要性
      • 什么是委托
      • 事件与委托的联系
      • 自定义委托的声明
      • 多播委托
      • 委托的缺点
      • Action委托和Func委托

声明:仅供个人学习使用,搬运自 哔哩哔哩SiKi学院

委托的重要性

事件的本质是【委托类型字段(Field-Like)】包装器,用来保护委托字段不被滥用,LAMBDA表达式的基础也是委托,LAMBDA表达式同时也是LINQ1的基础,只有理解了委托的一般使用情况,才能熟练地把一个LAMBDA表达式当做一个实际参数传递到方法中,

什么是委托

C#语言共有五种主要数据类型,而这五种类型又分为引用类型和值类型,引用类型有类、接口、委托,值类型包含结构体类型(struct、Vector2)和枚举类型(enum),即委托是一种类。
·
委托能够存储一个或多个方法的引用。简单来说就是当要用到一个或多个方法时,不需要直接调用,而是赋值给委托类型的变量,这个委托类型的变量将会存储、封装这些方法,然后由委托类型的变量通过Invoke或其它更加简便的调用形式来间接调用这些方法。

举个例子,当一个游戏结束后会提升主角的经验值以及奖励物品,还会出现游戏结束的UI和动画,而这些功能分别写在不同的方法之中,然后在GameManager方法之中来调用。
若使用委托,我们可以将要调用的方法赋值给委托类型的变量,然后再通过委托类型的变量调用这些方法。

using UnityEngine;

public class GameManager : MonoBehaviour
{
    public delegate void MyDelegate();
    MyDelegate myDelegate;
    private void Enable()
    {
        myDelegate += FindObjectOfType<MainPlayer>().GetBonus();
        myDelegate += FindObjectOfType<MainPlayer>().GetExp();
        myDelegate += FindObjectOfType<UIManager>().ShowWinCanvas();
        myDelegate += FindObjectOfType<AnimationManager>().AnimationPlay();
    }
    public void OnGameOver()
    {
        //使用委托调用
        myDelegate();
        /*直接调用*/
        /*FindObjectOfType<MainPlayer>().GetBonus();
        FindObjectOfType<MainPlayer>().GetExp();
        FindObjectOfType<UIManager>().ShowWinCanvas();
        FindObjectOfType<AnimationManager>().AnimationPlay();*/
    }
}
using UnityEngine;

public class MainPlayer : MonoBehaviour
{
    //奖励经验值和物品
    public int exp, bunus;
    public int expValue, bonusValue;
    public void GetExp()
    {
        exp += expValue;
    }
    public void GetBonus()
    {
        bonusValue += bonusValue;
    }
}

using UnityEngine;

public class UIManager : MonoBehaviour
{
    // 游戏胜利的画面
    public GameObject winPanel;
    public void ShowWinCanvas()
    {
        winPanel.SetActive(true);
    }    
}
using UnityEngine;
using UnityEngine.UI;

public class AnimationManager : MonoBehaviour
{
    //游戏胜利的动画
    public Animator winAnim;
    public void AnimationPlay()
    {
        winAnim.Play();
    }
}

事件与委托的联系

一个事件,看上去大多像一个委托类型的字段,但它只是一个委托类型字段的包装器、限制器,用来限制外界对这个委托类型字段的访问。
而委托类型的字段在通过事件包装、限制之后,外界只能访问它的+=、-=操作,只能添加、移除事件处理器

自定义委托的声明

类可以声明变量,可以创建实例(实例即对象,是类经过实例化之后得到的内存中的实体),即委托也可以,但委托的声明和一般的类不同,更像一般的方法的声明,与C语言函数指针相似,如下:

//声明委托类型
public delegate void MyDelegate();//这个委托类型可以指向任何一个返回值为空、参数列表为空的方法

C语言函数指针

//C语言声明一个函数指针
tepdef int(*Calculator)(int _x,int _y); 
/*C#委托的声明*/
public delegate void MyDelegate01(int _a,int _b);
private delegate int MyDelegate02(int _x,int _y);//这个委托指向一个返回值为int类型,参数列表为两个int类型的方法
private delegate double MyDelegate03(double _a);

int Add(int _a,int _b){
	return _a + _b;
}
int Multiply(int _a,int _b){
	return _a  *_b;
}
int mian(){
	int x=9;
	int y=11;
	/*直接调用*/
	printf("Add Result : %d\n",Add(x,y));
	printf("Multyply Result : %d\n",Multiply(x,y));
	/*通过指针调用*/
	Caculator pointer01 = &Add;
	Caculator pointer02 = &Multiply;
	printf("Add Result (pointer01): %d\n",pointer01(x,y));
	printf("Multyply Result (pointer02): %d\n",pointer02(x,y));
	return 0;
}

多播委托

一个委托内部封装了多个方法,例如本文刚开始的例子,便是多播委托

using UnityEngine;

public class GameManager : MonoBehaviour
{
    public delegate void MyDelegate();
    MyDelegate myDelegate;
    private void Enable()
    {
        myDelegate += FindObjectOfType<MainPlayer>().GetBonus();
        myDelegate += FindObjectOfType<MainPlayer>().GetExp();
        myDelegate += FindObjectOfType<UIManager>().ShowWinCanvas();
        myDelegate += FindObjectOfType<AnimationManager>().AnimationPlay();
    }
    public void OnGameOver()
    {
        myDelegate();        
    }
}

但是每个方法的返回值类型都要与委托类型的返回值类型兼容,比如我们声明了一个void的委托类型public delegate void MyDelegate(),要调用的方法如下:

private string Log()
{
	return DateTime.UtcNow.ToString();
}

由于Log()方法的返回值类型是string,与void类型不兼容,所以不能调用Log()方法

委托的缺点

容易导致内存泄漏
当委托调用的是实例方法(静态方法)时,这个被调用的方法必须存在于内存之中,即便没有其他引用变量引用该方法,这个方法的内存也不能被释放,一旦释放,委托便不能再间接调用对象的方法了,可能导致内存泄漏,随着泄漏的内存越来越多,程序性能会下降,直至程序崩溃,这些缺点(将+=写成=)是事件、观察者模式出现的原因

Action委托和Func委托

C#类库中有两个定义好的委托【Action委托】和【Func委托】,可以用来代替delegate关键字自定义委托的类型。

  • Action委托无返回值,可以有参数列表
  • Func委托由返回值,可以有参数列表
    举例如下:
//delegate关键字定义委托
public delegate void MyDelegate01();
public delegate string MyDelegate02();
public delegate double MyDelegate03(double _numA,double _numB);
MyDelegate01 myDelegate01;
MyDelegate02 myDelegate02;
MyDelegate03 myDelegate03;

private void Start()
{
	myDelegate01+=new MyDelegate01(Teleport);
	myDelegate02+=new MyDelegate02(Log);
	myDelegate03+=new MyDelegate(Add);	
}
private void Update()
{
	myDelegate01.Invoke();
	print(myDelegate02());
	print(myDelegate03(2.2, 7.89f));
}
private void Teleport()
{
    Vector3 currentPos = transform.position;
    currentPos.x = UnityEngine.Random.Range(-5f, 5f);
    transform.position = currentPos;
}
private string Log()
{
	return System.DateTime.UtcNow.ToString();
}
private double Add(Double _num1,double _num2)
{
	return _num1+_num2;
}
//不使用自定义委托
Action action01;
Func<string> func01;
Func<double,double,double> func02;
private void OnEnable()
{
	action01=new Action(Teleport);
	func01=new Func<string>(Log);
	func02=new Func<double,double,double>(Add);
}

private void Start()
{
	action01();
	func01();
	func01(2.2, 7.89f);
}

private void Teleport()
{
    Vector3 currentPos = transform.position;
    currentPos.x = UnityEngine.Random.Range(-5f, 5f);
    transform.position = currentPos;
}
private string Log()
{
	return System.DateTime.UtcNow.ToString();
}
private double Add(Double _num1,double _num2)
{
	return _num1+_num2;
}

  1. LINQ:.Net的“Language Integrated Quary”,较常用的有:Where/Select,都是以LamBDA形式显示在代码中 ↩︎



这篇关于C#委托类型的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程