ILRuntimeDemo理解

启动HotFix

  1. 使用AppDomain加载热更程序集dll及pdb(正式发布时请将PDB去掉)
  2. 初始化ILRuntime,(设置ILRuntime主线程的线程ID ,监听调试端口等),然后做一些ILRuntime的注册
  3. 使用热更

调用Invocation

//1.通过AppDomain调用方法
//参数:类型全名(命名空间+类型)、方法名、实例、实参列表
appdomain.Invoke("XX.XXX", "XXXX", null, null);


//2.通过IMethod调用方法
//预先获得IMethod,可以减低每次调用查找方法耗用的时间
IType type = appdomain.LoadedTypes["XX.XXX"];
//根据方法名称和参数个数获取方法
IMethod method = type.GetMethod("XXXX", 1);
//使用IMethod调用(参数:IMethod、实例、实参列表)
appdomain.Invoke(method, null, 123);


//3.通过无GC Alloc方式调用方法
//无GC Alloc方式参数压入顺序:先将带ref/out关键字的形参参数按顺序压入初始值->若为成员方法压入this->按形参顺序压入实参
using (var ctx = appdomain.BeginInvoke(method))
{
	//将参数进行压栈
    ctx.PushInteger(123);
    //调用
    ctx.Invoke();
}


//4.指定参数类型来获得IMethod
IType intType = appdomain.GetType(typeof(int));
//参数类型列表
List<IType> paramList = new List<ILRuntime.CLR.TypeSystem.IType>();
paramList.Add(intType);
//根据方法名称和参数类型列表获取方法(也可加入泛型参数类型列表获取泛型方法)
method = type.GetMethod("XXXX", paramList, null);
appdomain.Invoke(method, null, 456);

//5.调用成员方法
//实例化(参数:类型全名、构造参数)
object obj = appdomain.Instantiate("XX.XXX", new object[] { 233 });
//通过ILType实例化
object obj2 = ((ILType)type).Instantiate();
//调用成员方法(属性器Get方法)
method = type.GetMethod("get_XXXXX", 0);
using (var ctx = appdomain.BeginInvoke(method))
{
	//将对象压栈后调用
    ctx.PushObject(obj);
    ctx.Invoke();
    //获取返回值
    int id = ctx.ReadInteger();
}
using (var ctx = appdomain.BeginInvoke(method))
{
	//将对象压栈后调用
    ctx.PushObject(obj2);
    ctx.Invoke();
    //获取返回值
    int id = ctx.ReadInteger();
}


//6.调用泛型方法
IType stringType = appdomain.GetType(typeof(string));
IType[] genericArguments = new IType[] { stringType };
//参数:类型全名(命名空间+类型)、方法名、泛型参数类型列表、实例、实参列表
appdomain.InvokeGenericMethod("XX.XXX", "XXXX", genericArguments, null, "TestString");


//7.获取泛型方法的IMethod
paramList.Clear();
paramList.Add(intType);
genericArguments = new IType[] { intType };
//根据参数类型列表及泛型参数类型列表来获取对应IMthod
method = type.GetMethod("XXXX", paramList, genericArguments);
appdomain.Invoke(method, null, 33333);


//8.通过无GC Alloc方式调用带Ref/Out参数的方法
//被调用方法:public void RefOutMethod(int addition, out List<int> lst, ref int val)
method = type.GetMethod("RefOutMethod", 3);
int initialVal = 500;
using(var ctx = appdomain.BeginInvoke(method))
{
    //第一个ref/out参数初始值
    ctx.PushObject(null);
    //第二个ref/out参数初始值
    ctx.PushInteger(initialVal);
    //压入this
    ctx.PushObject(obj);
    //压入参数1:addition
    ctx.PushInteger(100);
    //压入参数2: lst,由于是ref/out,需要压引用,这里是引用0号位,也就是第一个PushObject的位置
    ctx.PushReference(0);
    //压入参数3,val,同ref/out
    ctx.PushReference(1);
    ctx.Invoke();
    //读取0号位的值
    List<int> lst = ctx.ReadObject<List<int>>(0);
    initialVal = ctx.ReadInteger(1);
}

委托Delegate

在ILRuntime中,若需要跨域调用委托,则需要注册适配器。
使用Action或者Func当作委托类型可以避免写转换器,但需要注册参数搭配。
完全在热更DLL内部使用的委托,直接可用,不需要做任何处理

//注册委托参数搭配
appdomain.DelegateManager.RegisterMethodDelegate<int>();
appdomain.DelegateManager.RegisterFunctionDelegate<int, string>();

如需要注册适配器,则在注册参数代码后注册转换器,将Action或者Func转换成目标委托类型即可。

//注册适配转换器
appdomain.DelegateManager.RegisterDelegateConvertor<UnityEngine.Events.UnityAction<float>>((action) =>
{
    return new UnityEngine.Events.UnityAction<float>((a) =>
    {
        ((System.Action<float>)action)(a);
    });
});

继承Inheritance

跨域继承需要生成适配器,并且在ILRuntime初始化完成后进行注册。

//参数为所需跨域继承类型的适配器实例
appdomain.RegisterCrossBindingAdaptor(new XXXAdapter());

在自己实现的ILRuntime.Moudle中添加了跨域继承类型的配置功能。

CLR重定向CLRRedirection

当我们需要挟持原方法实现,添加一些热更DLL中的特殊处理的时候,就需要CLR重定向了。

//获取参数类型符合要求的MethodInfo
var mi = typeof(Debug).GetMethod("Log", new System.Type[] { typeof(object) });
//参数:要重定向的方法的MethodInfo、重定向到的方法
appdomain.RegisterCLRMethodRedirection(mi, Log_11);

CLR绑定CLRBinding

默认情况下,从热更DLL里调用Unity主工程的方法,是通过反射的方式调用的,这个过程中会产生GC Alloc,并且执行效率会偏低。
ILRuntime通过CLR方法绑定机制,可以选择性的对经常使用的CLR接口进行直接调用,从而尽可能的消除反射调用开销以及额外的GC Alloc。

//在生成CLRBinding代码后,在ILRuntime CLR重定向注册之后调用以下方法,即可使用CLR绑定
ILRuntime.Runtime.Generated.CLRBindings.Initialize(appDomain);

协程Coroutine

使用Couroutine时,C#编译器会自动生成一个实现了IEnumerator,IEnumerator,IDisposable接口的类,因为这是跨域继承,所以需要写CrossBindAdapter。
跨域继承只能有1个Adapter,因此应该尽量避免一个类同时实现多个外部接口,对于coroutine来说是IEnumerator,IEnumerator和IDisposable。
ILRuntime虽然支持,但是一定要小心这种用法,使用不当很容易造成不可预期的问题。
日常开发如果需要实现多个DLL外部接口,请在Unity这边先做一个基类实现那些个接口,然后继承那个基类。

使用了插件作者的CoroutineAdapter,尝试了以下,可以在热更dll内随意使用协程,但是CoroutineAdapter无法进行CLR绑定。

MonoBehaviour

在热更DLL里面使用MonoBehaviour是可以做到的。
需要在MonoBehaviourAdaptor中实现对应的事件方法。
为了在热更DLL里正常使用AddComponent和GetComponent需要重定向这两个方法。
也可以实现通过MonoBehaviourAdaptor在Unity获取到热更dll类型。

反射Reflection

C#工程中反射是一个非常经常用到功能,ILRuntime也对反射进行了支持,在热更DLL中使用反射跟原生C#没有任何区别。
在Unity中使用AppDomain获取DLL中类型后,使用IType的ReflectionType可获取热更DLL内的真正类型,之后正常使用反射调用方法,字段,属性即可。

序列化LitJson

非常简单的序列化反序列化,但是对LitJson源码是需要进行处理的。这里插件作者已经整理好了。

值类型绑定ValueTypeBinding

值类型绑定内部对值类型的操作进行了重定向。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值