UnityAction-vs-System.Action

在 Unity 中我们可以使用 Unity Mono 中的 System.Action, 也可以是使用 UnityEngine.Events.UnityAction,那么这两者有什么区别呢?
其实,两者在实现上没有区别,我们可以看看他们的源码:

namespace UnityEngine.Events
{
  public delegate void UnityAction<T0, T1, T2, T3>(T0 arg0, T1 arg1, T2 arg2, T3 arg3);
}
namespace System
{
  public delegate void Action<T1, T2, T3, T4>(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
}

可以看到,除了 Namespace 不同之外,它们是完全等价的。这里需要注意一下, Unity Mono 的 System.Action 和 .NET 的 System.Action 是有区别的,.Net System.Action 最多可以接受16个参数,而 Unity Mono 的最多只有4个参数。


.NET System.Action

public delegate void Action<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, in T13, in T14, in T15, in T16>(
    T1 arg1,
    T2 arg2,
    T3 arg3,
    T4 arg4,
    T5 arg5,
    T6 arg6,
    T7 arg7,
    T8 arg8,
    T9 arg9,
    T10 arg10,
    T11 arg11,
    T12 arg12,
    T13 arg13,
    T14 arg14,
    T15 arg15,
    T16 arg16
)

既然 UnityAction 和 System.Action 等价,为什么 Unity 还要再实现一个 UnityAction 呢?干嘛不直接使用 System.Action 啊?答案在于 Unity 的代码裁剪功能。如下图,如果你使用的 Mono Backend ,你可以选择使用 micro mscorlib 级别的代码裁剪。micro mscorlib 是把 mscorlib 中的部分代码移除了,来减少包体的大小,而 System.Action 并不在 micro mscorlib 中,所以Unity 需要引入 UnityAction 来解决这个问题。

如果你使用的是 IL2CPP Backend,那么你应该看不到这个 Stripping Level 的选项了,取而代之的是 Stripping Engine Code 这个选项。IL2CPP 会把所有的 DLL 和 C#代码转变成 C++ 代码,然后再使用传统的 C++ 代码优化器进行裁剪和优化。

我们在 Unity Android 下面看看 micro mscorlib 和 disable 代码裁剪级别生成出来的 APK 的区别,下面是对 APK 拆包后的对比。第一个图是 micro mscorlib, 第二个图是disable 的。我们可以看到 micro mscorlib 级别的代码裁剪确实完全裁剪掉了很多的的 DLL,没有完全裁剪掉的 DLL,其 DLL 内部也裁掉了很多代码,比如像 mscorlib.dll 就从2.4MB减少到了1.5MB。