跨域继承
跨域继承,即是热更工程继承主工程里面的类。
首先在ILRuntimeManager
添加一个抽象类
1 2 3 4 5 6 7 8 9 10 11 12
| public abstract class TestBaseClass { public virtual void TestVirtual(string str) { Debug.Log("TestBaseClass TestVirtual str=" + str); } public abstract void TestAbstract(int a); public virtual int Value { get { return 0; } } }
|
然后再在HotFix热更工程里添加TestInheritance
新类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| using UnityEngine;
namespace HotFix { public class TestInheritance : TestBaseClass { public override void TestVirtual(string str) { base.TestVirtual(str); Debug.Log("TestInheriance TestVirtual str = " + str); } public override void TestAbstract(int a) { Debug.Log("TestInheritance TestAbstract a = " + a); } public override int Value => base.Value; } }
|
修改ILRuntimeManager
的OnHotFixLoaded
方法
1 2 3 4 5
| void OnHotFixLoaded() { TestBaseClass obj = m_AppDomain.Instantiate<TestBaseClass>("HotFix.TestInheritance"); }
|
跨域继承适配器
跨域继承适配器需要两个类,一个是跨域绑定类,一个是适配器类
跨域继承适配器绑定类
绑定类属于工具类,帮助ILRuntime在运行时获取到主程序集里面需要被继承的类(通过BaseCLRType
属性),以及需要被继承的类的适配器(通过AdaptorType
属性),以及提供一个适配器实例的方法(通过CreateCLRInstance
方法)。
在ILRuntimeManager
里面新建一个InheritanceAdaptor
类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| public class InheritanceAdaptor : CrossBindingAdaptor { public override System.Type BaseCLRType { get { return typeof(TestBaseClass); } } public override System.Type AdaptorType { get { return typeof(Adaptor); } } public override System.Type[] BaseCLRTypes { get { return null; } } public override object CreateCLRInstance(AppDomain appdomain, ILTypeInstance instance) { return new Adaptor(appdomain,instance); } class Adaptor : TestBaseClass, CrossBindingAdaptorType { } }
|
适配器类
适配器类一般是适配器绑定类的内部类,也就是上面的class Adaptor
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
| public class Adaptor : TestBaseClass, CrossBindingAdaptorType { private AppDomain m_AppDomain; private ILTypeInstance m_ILTypeInstance; private IMethod m_TestAbstractMethod; private IMethod m_TestVirtualMethod; private IMethod m_GetValue; private IMethod m_ToStringMethod; object[] param1 = new object[1]; private bool m_IsTestVirtualInvoking = false; private bool m_IsGetValueInvoking = false; public Adaptor() { } public Adaptor(AppDomain appdomain, ILTypeInstance instance) { m_AppDomain = appdomain; m_ILTypeInstance = instance; } public ILTypeInstance ILInstance { get { return m_ILTypeInstance; } } public override void TestAbstract(int a) { if (m_TestAbstractMethod == null) { m_TestAbstractMethod = m_ILTypeInstance.Type.GetMethod("TestAbstract",1); }
if (m_TestAbstractMethod != null) { param1[0] = a; m_AppDomain.Invoke(m_TestAbstractMethod, m_ILTypeInstance, param1); } } public override void TestVirtual(string str) { if (m_TestVirtualMethod == null) { m_TestVirtualMethod = m_ILTypeInstance.Type.GetMethod("TestVirtual", 1); } if (m_TestVirtualMethod != null && !m_IsTestVirtualInvoking) { m_IsTestVirtualInvoking = true; param1[0] = str; m_AppDomain.Invoke(m_TestVirtualMethod, m_ILTypeInstance, param1); m_IsTestVirtualInvoking = false; } else { base.TestVirtual(str); } } public override int Value { get { if(m_GetValue == null) { m_GetValue = m_ILTypeInstance.Type.GetMethod("get_Value"); } if(m_GetValue != null && !m_IsGetValueInvoking) { m_IsGetValueInvoking = true; int res = (int)m_AppDomain.Invoke(m_GetValue, m_ILTypeInstance); m_IsGetValueInvoking = false; return res; } else { return base.Value; } } } public override string ToString() { if (m_ToStringMethod == null) { m_ToStringMethod = m_AppDomain.ObjectType.GetMethod("ToString",0); } IMethod m = m_ILTypeInstance.Type.GetVirtualMethod(m_ToStringMethod); if (m != null || m is ILMethod) { return m_ILTypeInstance.ToString(); } else { return m_ILTypeInstance.Type.FullName; } } }
|
适配器类理解
ILRuntime在运行时只能获取到热更dll的字节码,这意味着它无权获取到主工程里面的类,所以在热更类继承主工程的类属于“假继承”。
这就是我们写适配器类的原因,适配器类是Unity主工程内真正继承对应基类的子类,只不过这个类变相调用了热更中对应的,也继承了基类的子类,有了这个中转,我们才能模拟出“继承”的效果。
这样我们在主工程内调用热更工程中子类的方法,ILRuntime会查找这个适配器,然后调用适配器。
一般来讲,适配器类只需要写明基类里面的虚方法或抽象方法的逻辑,其余的基类里不需要子类覆盖的方法和属性等不需要写进适配器里。
只要子类重载方法里有调用base.
的地方,在适配器里面就必须声明好“m_IsInvoking”变量。
调用子类
跨域继承适配器写好后,我们在ILRuntimeManager
的InitializeILRuntime
方法中注册这个适配器,就像之前注册委托适配器一样。
1 2 3 4 5 6
| void InitializeILRuntime() { m_AppDomain.RegisterCrossBindingAdaptor(new InheritanceAdaptor()); }
|
最后在ILRuntimeManager
的OnHotFixLoaded
方法中调用子类即可
1 2 3 4 5 6 7 8
| void OnHotFixLoaded() { TestBaseClass obj = m_AppDomain.Instantiate<TestBaseClass>("HotFix.TestInheritance"); obj.TestAbstract(556); obj.TestVirtual("ATAO"); Debug.Log(obj.Value); }
|
第二种调用方法
在上面的调用中可以发现,我们通过跨域继承直接调用基类的方式来调用热更工程里面子类的方法。
我们可以在子类中写一个静态方法,像单例模式一样,用于返回子类的实例
修改HotFix工程的TestInheritance
类
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| using UnityEngine;
namespace HotFix { public class TestInheritance : TestBaseClass { public static TestInheritance GetInstance() { return new TestInheritance(); } } }
|
这样的话,我们修改ILRuntimeManager
的OnHotFixLoaded
方法,使用AppDomain.Invoke
这个API来获取实例
1 2 3 4 5 6 7 8
| void OnHotFixLoaded() { TestBaseClass obj = m_AppDomain.Invoke("HotFix.TestInheritance","GetInstance",null,null) as TestBaseClass; obj.TestAbstract(556); obj.TestVirtual("ATAO"); Debug.Log(obj.Value); }
|