扩展方法 Extension Method 咱们不少时候都是考虑方便性才去添加的, 系统库也有不少, 像 Linq / Expression 之类的, 使用起来就像是给对象添加了一个成员函数同样 : 函数
官方例子this
namespace ExtensionMethods { public static class IntExtensions { public static bool IsGreaterThan(this int i, int value) { return i > value; } } }
using ExtensionMethods; class Program { static void Main(string[] args) { int i = 10; bool result = i.IsGreaterThan(100); Console.WriteLine(result); } }
看到扩展的函数调用就像成员变量同样, 不过真的是这样吗? 看看下面的代码 : spa
using UnityEngine; using System; public class Test : MonoBehaviour { private System.Action aCall = null; private void Start() { aCall.Call(); } } public static class Ext { public static void Call(this System.Action action) { if(action != null) { action.Invoke(); } } }
断点看, 可以进来 : code
那么它提供的扩展就不是代码层面的, 是编译层面的了, 在编译上全部的方法都是静态的, 只是在调用的时候传入了调用对象, 而成员函数只是在上面进行的封装, 从反射的Method.Invoke() 就能看到实例须要传入对象才能正确调用 : 对象
public object Invoke(object obj, object[] parameters)
其实它的代码等效于 : blog
aCall.Call(); // 等于 Ext.Call(aCall);
因此就算看起来是成员函数调用, 实际上是静态调用, 因此即便对象 aCall 是空, 也是能够运行的, 对于 Unity 来讲, 不少时候会发生非预期的对象删除, 或者删除后仍然存在的现象, 每次都须要判空, 就像一个 UI 上的 Text 这样 : string
public class Test : MonoBehaviour { public Text title; public void SetTitle(string info) { if(title) { title.text = info; } } }
这样只在功能内写判空的就比较累人, 不如写个静态方法 : it
public class Test : MonoBehaviour { public UnityEngine.UI.Text title; } public static class Ext { public static void SetTextUI(UnityEngine.UI.Text text, string info) { if(text) { text.text = info; } } } //... Text textUI; Ext.SetTextUI(textUI, "xxx");
不过如今发现扩展方法的调用也是静态调用, 空对象也能运行, 那就写成扩展就更方便了 : io
public static class Ext { public static void SetTextUI(this UnityEngine.UI.Text text, string info) { if(text) { text.text = info; } } } //... Text textUI; textUI.SetTextUI("xxx");
这就是扩展方法的好处了, 它不是代码层面的添加了一个成员函数.编译
还有一个如今用 C# 6.0 以上语法的话, 能够直接判空 :
Text textUI; textUI?.text = "xxx";
但是对于 Unity Object 对象, 这样的判空至关于 :
Text textUI; if(textUI != null) { textUI.text = "xxx"; }
这样判空是不对的, 必须使用它的隐式转换 bool 来判断, 想要这个功能的正确实现, 只有经过修改语法树的方法来尝试了...