UWA问答 | 博客 | 游戏及VR应用性能优化记录分享 | 侑虎科技 - 匿名函数
type: channel
link: https://blog.uwa4d.com/tag/匿名函数/
items: 我所理解的委托和匿名函数

我所理解的委托和匿名函数
type: item
date: 2017-10-12T09:49:00+03:00
description: 一、引言 委托和匿名函数在我们的日常编程中用得非常多，甚至有些同学用了大半年的匿名函数，却对其本质不甚了解，就像我们天天都要呼吸空气，却对空气质量和成分却没清晰的认识。委托和匿名函数虽然使用起来非常方便，但由于使用者理解不够深刻，不假思索地使用带来更多的是内存泄漏和过多的垃圾产生。那么这里，我就用一种我所理解的方式来给大家挖挖委托匿名函数的原理，让大家看看为什么这家伙用起来这么容易让人上瘾。 本文讲解的主要内容如下： 1）什么是委托和匿名函数； 2）实现的底层原理； 3）容易引起的问题； 4）如何正确高效地使用匿名函数； 5）如何实现一个不产生内存垃圾的通用委托。 二、什么是委托？ 委托是一种引用类型，可以指向任意函数（无论静态动态，公开与私有，虚方法还是非虚方法），从而实现委托调用。（这里讨论的委托不考虑多播事件） C#中声明形式如下： delegate int DelegateType( int a, int b ); C#的委托来源于C语言中的函数指针，我们来看看C#的母亲语言C++中，函数指针是什么样的： // 普通静态函数指针 typedef int ( *FP_ADD )( int a, int b ); // 成员函数指针 typedef int( TestClass::*FP_ADD2 )( int a, int b ); C++中的函数指针形式复杂，各种类型之间不能相互转型，用起来特别麻烦，尤其引入调用协议、成员方法、虚方法，甚至虚继承，多重继承的时候更是个难题。由于语言的标准并没有明确如何来实现调用机制，因此各个编译器厂商在实现各类函数调用的实现上不尽相同，例如一个虚函数指针，其携带的信息不仅仅只有函数入口地址那么简单，可能还包含有转型到派生类的偏移地址和虚函数表的索引。 C++到C#实现了一次语言进化，语言的标准强制了委托的行为一致性，更是让多数初级用户也能运用自如。 一位聪明的程序员，他可能觉得C++中的函数指针使用起来实在太麻烦，他眼红着隔壁的C#程序员无脑的用着语言为他们提供的Delegate，他发誓要在号称无敌的C++语言中也实现这么一个Delegate，造福子孙后代，于是他废寝忘食，终于成功研发了FastDelegate，这东西牛逼得一塌糊涂，不但好用，而且效率出奇的高，甚至超过了一些准库中的实现。一些编译器厂商纷纷向这位程序员讨教实现高效的函数调用的奥秘。https://www.codeproject.com/Articles/7150/Member-Function-Pointers-and-the-Fastest-Possible C#中如何使用委托： class TestClass { // 这里声明一个委托类型 public delegate int DelegateType( int a, int b ); // 一个成员变量 int m_plus = 1; // 一个静态函数 public static int Static_Add( int a, int b ) { return a + b; } // 一个成员函数 public int Member_Add( int a, int b ) { return a + b + m_plus; } public static int Add( int a, int b, DelegateType overrideFunc = null ) { if ( overrideFunc != null ) { return overrideFunc( a, b ); } return a + b; } public static void Test1() { // 传入一个静态成员方法 Add( 1, 2, TestClass.Static_Add ); } public static void Test2() { var obj = new TestClass(); // 传入一个动态实例方法 Add( 1, 2, obj.Member_Add ); } } 因为刚才讲到，委托是一种类型，而函数并不是类型，所以通常情况下，如果没有为两种类型提供转型操作，编译器会报出类型不匹配的错误。所以，既然我们这种写法能够通过编译器，那么说明编译器为我们实现了转型操作，比如隐式用于转型的构造函数。（知识点：隐式显示转型构造函数）。 如果委托的函数非常简单，或者基本上没有重复利用的可能，那么可以直接把函数体实现写在委托的生成处，这样你就不用费劲心思去思考该怎样给函数取一个容易的名字了，名字压根就不重要了，函数体那几句代码更易于理解代码在干什么，这就是匿名函数，也就是叫做lambda表达式的东西。 //匿名函数原始写法 Add( 1, 2, delegat…