UH4L5——调用ILRuntime中方法
UH4L5——调用ILRuntime中方法
本章代码关键字
1 | appDomain.Invoke() //除了传入IMethod,对于方法直接传入类名,方法名,要执行方法的对象(静态方法不传入),参数,即可执行方法 |
跨域调用方法
-
静态方法
静态方法调用的规则和成员属性方法调用规则基本类似,三板斧调用:
-
appdomain.Invoke("命名空间.类名", "静态方法名", null, 参数列表)
-
appdomain.Invoke(IMethod对象, null, 参数列表)
- 无GC Alloc方式:
using(BeginInvoke){ push Invoke read -> ubpir }
方式
注意:建议大家都使用类似反射的
IMethod
来调用方法,并且使用更节约性能的无GC Alloc方式来调用 -
-
成员方法
三板斧调用(和静态方法调用区别就是,需要指明调用方法的对象)
-
appdomain.Invoke("命名空间.类名", "静态方法名", 对象, 参数列表)
-
appdomain.Invoke(IMethod对象, 对象, 参数列表)
- 无GC Alloc方式:
using(BeginInvoke){ push Invoke read -> ubpir }
方式
-
-
重载方法
- 方法调用,还是遵循三板斧调用规则
- 参数数量不同时,通过明确参数数量来明确重载,参数数量相同,类型不同时,通过指明参数类型来明确重载
-
ref
/out
方法-
ref
/out
方法只能通过无GC Alloc方法调用using(BeginInvoke){ push Invoke read -> ubpir }
方式 -
在调用时多了三个步骤
- 需要先压入
ref
或out
参数的初始值 - 压入参数环节压入引用索引值
- 通过
Read
按顺序获取ref
、out
参数,返回值最后获取
- 需要先压入
-
静态方法调用
假设要调用热更新工程内的如下的静态方法(热更工程内可以直接调用UnityEngine
命名空间内的内容):
1 | using UnityEngine; |
静态方法调用有两种方式
-
直接通过
appDomain.Invoke("命名空间.类名", "静态方法名", null, 参数列表)
,调用静方法这里是不传入
IMethod
的重载,参数如下:- 参数一:
"命名空间.类名"
- 参数二:方法名
- 参数三:要执行方法的对象,如果是静态方法则不填
- 后续参数:参数列表
该方法除了静态方法,还可以调用:成员方法
1
2
3
4
5
6
7
8
9
10
11
12void Start()
{
ILRuntimeMgr.Instance.StartILRuntime(() =>
{
AppDomain appDomain = ILRuntimeMgr.Instance.appDomain;
//无参方法调用
appDomain.Invoke("HotFix_Project.Lesson3_Test", "TestStaticFun", null, null);
//有参有返回值调用
int i = (int)appDomain.Invoke("HotFix_Project.Lesson3_Test", "TestStaticFun", null, 99);
print(i);
});
}输出:
- 参数一:
-
通过类似反射的
IMethod
调用静态方法通过
IType
中的GetMethod
方法,类似反射一样的获取对应类中的静态方法1
2
3
4
5
6AppDomain appDomain = ILRuntimeMgr.Instance.appDomain;
IType type = appDomain.LoadedTypes["HotFix_Project.Lesson3_Test"];
//无参无返回值
IMethod method1 = type.GetMethod("TestStaticFun", 0);
//有参有返回值
IMethod method2 = type.GetMethod("TestStaticFun2", 1);-
通过
appDomain.Invoke(方法名,null,参数)
调用 静态方法1
2
3
4
5
6
7
8
9
10
11
12
13
14
15void Start()
{
ILRuntimeMgr.Instance.StartILRuntime(() =>
{
AppDomain appDomain = ILRuntimeMgr.Instance.appDomain;
IType type = appDomain.LoadedTypes["HotFix_Project.Lesson3_Test"];
IMethod method1 = type.GetMethod("TestStaticFun", 0);
IMethod method2 = type.GetMethod("TestStaticFun2", 1);
//无参无返回值
appDomain.Invoke(method1, null, null);
//有参有返回值
int i2 = (int)appDomain.Invoke(method2, null, 88);
print(i2);
});
}输出:
-
通过更节约性能的无GC Alloc方式(调用完后直接回收)调用静态方法,类似上节课的成员属性
1
2
3
4
5
6using (var method = appDomain.BeginInvoke(methodName))
{
method.Push.....(1000);//传入指定类型参数
method.Invoke(); //执行方法
method.Read....() //获取指定类型返回值
}使用示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24void Start()
{
ILRuntimeMgr.Instance.StartILRuntime(() =>
{
AppDomain appDomain = ILRuntimeMgr.Instance.appDomain;
IType type = appDomain.LoadedTypes["HotFix_Project.Lesson3_Test"];
IMethod method1 = type.GetMethod("TestStaticFun", 0);
IMethod method2 = type.GetMethod("TestStaticFun2", 1);
using (var method = appDomain.BeginInvoke(method1))
{
method.Invoke();
}
using (var method = appDomain.BeginInvoke(method2))
{
method.PushInteger(77);
method.Invoke();
int i3 = method.ReadInteger();
print(i3);
}
});
}输出:
-
成员方法调用
假设要调用热更新工程内的如下的成员方法(热更工程内可以直接调用UnityEngine
命名空间内的内容):
1 | using UnityEngine; |
成员方法调用和静态方法调用几乎一样,区别就是需要先创建对象,将对象传入之前为null
的地方
-
直接通过
appDomain.Invoke("命名空间.类名", "方法名", 类对象, 参数列表)
1
2
3
4
5
6
7
8
9
10
11
12
13void Start()
{
ILRuntimeMgr.Instance.StartILRuntime(() =>
{
AppDomain appDomain = ILRuntimeMgr.Instance.appDomain;
IType type = appDomain.LoadedTypes["HotFix_Project.Lesson3_Test"];
object obj = ((ILType)type).Instantiate();
appDomain.Invoke("HotFix_Project.Lesson3_Test", "TestFun", obj, null);
int i = (int)appDomain.Invoke("HotFix_Project.Lesson3_Test", "TestFun2", obj, 99);
print(i);
});
}输出:
-
通过类似反射的
IMethod
调用成员方法通过
IType
中的GetMethod
方法,类似反射一样的获取对应类中的方法1
2
3
4AppDomain appDomain = ILRuntimeMgr.Instance.appDomain;
IType type = appDomain.LoadedTypes["HotFix_Project.Lesson3_Test"];
IMethod method1 = type.GetMethod("TestFun", 0);
IMethod method2 = type.GetMethod("TestFun2", 1);-
通过
appDomain.Invoke(IMethod对象, 类对象, 参数列表)
调用 成员方法1
2
3
4
5
6
7
8
9
10
11
12
13
14void Start()
{
ILRuntimeMgr.Instance.StartILRuntime(() =>
{
AppDomain appDomain = ILRuntimeMgr.Instance.appDomain;
IType type = appDomain.LoadedTypes["HotFix_Project.Lesson3_Test"];
IMethod method1 = type.GetMethod("TestFun", 0);
IMethod method2 = type.GetMethod("TestFun2", 1);
appDomain.Invoke(method1, obj);
int i2 = (int)appDomain.Invoke(method2, obj, 88);
print(i2);
});
}输出:
-
通过更节约性能的GC Alloc方式(调用完后直接回收)调用 成员方法,类似上节课的成员属性
调用成员方法,我们必须调用
InvocationContext.PushObject()
来压入要执行方法的对象1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25void Start()
{
ILRuntimeMgr.Instance.StartILRuntime(() =>
{
AppDomain appDomain = ILRuntimeMgr.Instance.appDomain;
IType type = appDomain.LoadedTypes["HotFix_Project.Lesson3_Test"];
IMethod method1 = type.GetMethod("TestFun", 0);
IMethod method2 = type.GetMethod("TestFun2", 1);
using (var method = appDomain.BeginInvoke(method1))
{
method.PushObject(obj);
method.Invoke();
}
using (var method = appDomain.BeginInvoke(method2))
{
method.PushObject(obj);
method.PushInteger(77);
method.Invoke();
int i3 = method.ReadInteger();
print(i3);
}
});
}输出:
-
重载方法调用
假设要调用热更新工程内的如下的重载方法(热更工程内可以直接调用UnityEngine
命名空间内的内容):
1 | using UnityEngine; |
-
参数数量不同
-
通过
appDomain.Invoke
调用参数数量不同的重载方法时,传入不同数量的参数即可自动分别调用不同参数1
2
3
4
5
6
7
8
9
10
11
12void Start()
{
ILRuntimeMgr.Instance.StartILRuntime(() =>
{
AppDomain appDomain = ILRuntimeMgr.Instance.appDomain;
IType type = appDomain.LoadedTypes["HotFix_Project.Lesson3_Test"];
object obj = ((ILType)type).Instantiate();
appDomain.Invoke("HotFix_Project.Lesson3_Test", "TestFun", obj);
appDomain.Invoke("HotFix_Project.Lesson3_Test", "TestFun", obj, 1);
});
}但当存在数量相同而参数类型不同的重载时,就会因为无法明确调用哪种重载而报错
1
2
3
4
5
6
7
8
9
10
11
12
13void Start()
{
ILRuntimeMgr.Instance.StartILRuntime(() =>
{
AppDomain appDomain = ILRuntimeMgr.Instance.appDomain;
IType type = appDomain.LoadedTypes["HotFix_Project.Lesson3_Test"];
object obj = ((ILType)type).Instantiate();
appDomain.Invoke("HotFix_Project.Lesson3_Test", "TestFun", obj);
appDomain.Invoke("HotFix_Project.Lesson3_Test", "TestFun", obj, 1);
appDomain.Invoke("HotFix_Project.Lesson3_Test", "TestFun", obj, 1.1f);
});
}输出:
-
通过
GetMethod
的第二个参数来获取对应参数个数的函数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
28void Start()
{
ILRuntimeMgr.Instance.StartILRuntime(() =>
{
AppDomain appDomain = ILRuntimeMgr.Instance.appDomain;
IType type = appDomain.LoadedTypes["HotFix_Project.Lesson3_Test"];
object obj = ((ILType)type).Instantiate();
appDomain.Invoke("HotFix_Project.Lesson3_Test", "TestFun", obj);
appDomain.Invoke("HotFix_Project.Lesson3_Test", "TestFun", obj, 1);
IMethod method1 = type.GetMethod("TestFun", 0);
IMethod method2 = type.GetMethod("TestFun", 1);
using (var method = appDomain.BeginInvoke(method1))
{
method.PushObject(obj);
method.Invoke();
}
using (var method = appDomain.BeginInvoke(method2))
{
method.PushObject(obj);
method.PushInteger(1);
method.Invoke();
}
});
}但同样的,当存在数量相同而参数类型不同的重载时,就会因为无法明确调用哪种重载而报错
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
38void Start()
{
ILRuntimeMgr.Instance.StartILRuntime(() =>
{
AppDomain appDomain = ILRuntimeMgr.Instance.appDomain;
IType type = appDomain.LoadedTypes["HotFix_Project.Lesson3_Test"];
object obj = ((ILType)type).Instantiate();
appDomain.Invoke("HotFix_Project.Lesson3_Test", "TestFun", obj);
appDomain.Invoke("HotFix_Project.Lesson3_Test", "TestFun", obj, 1);
//appDomain.Invoke("HotFix_Project.Lesson3_Test", "TestFun", obj, 1.1f);
IMethod method1 = type.GetMethod("TestFun", 0);
IMethod method2 = type.GetMethod("TestFun", 1);
IMethod method3 = type.GetMethod("TestFun", 1);
using (var method = appDomain.BeginInvoke(method1))
{
method.PushObject(obj);
method.Invoke();
}
using (var method = appDomain.BeginInvoke(method2))
{
method.PushObject(obj);
method.PushInteger(1);
method.Invoke();
}
using (var method = appDomain.BeginInvoke(method3))
{
method.PushObject(obj);
method.PushInteger(1.1f);
method.Invoke();
}
});
}输出:
-
-
参数数量相同,类型不同,通过上面两种方式直接使用无法确定取出来的函数哪种重载,我们需要通过
GetMethod
方法来获取指定参数类型的函数-
获取参数对应的
IType
类型,利用appDomain
中的GetType
方法 获取指定变量类型的IType
1
IType floatType = appDomain.GetType(typeof(float));
-
放入参数列表中,将获取到的
IType
放入List<IType>
中1
2List<IType> paramslist = new List<IType>();
paramslist.Add(floatType); -
传入
GetMethod
中获取指定类型参数,使用GetMethod
的另一个重载,传入指定类型获取方法信息1
IMethod method3 = type.GetMethod("TestFun", paramslist, null);
使用示例:
假设明确要调用参数列表为
float
的重载方法1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21void Start()
{
ILRuntimeMgr.Instance.StartILRuntime(() =>
{
AppDomain appDomain = ILRuntimeMgr.Instance.appDomain;
IType type = appDomain.LoadedTypes["HotFix_Project.Lesson3_Test"];
object obj = ((ILType)type).Instantiate();
//明确要获取参数列表为float的重载
IType floatType = appDomain.GetType(typeof(float));
List<IType> paramslist = new List<IType>();
paramslist.Add(floatType);
IMethod method3 = type.GetMethod("TestFun", paramslist, null);
using (var method = appDomain.BeginInvoke(method3))
{
method.PushObject(obj);
method.PushFloat(5.5f);
method.Invoke();
}
});
}输出:
-
ref
/ out
方法调用
假设要调用热更新工程内的如下的重载方法(热更工程内可以直接调用UnityEngine
命名空间内的内容):
1 | using System.Collections.Generic; |
需要通过IMethod
方法调用,并且只能使用无GC Alloc方法调用
-
和其他函数不一样的地方,需要先压入
ref
或out
参数的初始值1
2
3
4
5
6
7using (var method = appDomain.BeginInvoke(methodInfo))
{
//压入第一个ref参数的初始值
method.PushObject(list);
//压入第一个out参数的初始值,由于out参数不需要在外部初始化 所以压入null即可
method.PushObject(null);
} -
和其它函数调用写法一致,压入调用对象,压入各参数
1
2
3
4
5
6
7
8
9
10
11
12using (var method = appDomain.BeginInvoke(methodInfo))
{
//压入第一个ref参数的初始值
method.PushObject(list);
//压入第一个out参数的初始值,由于out参数不需要在外部初始化 所以压入null即可
method.PushObject(null);
//其他函数调用写法一致。压入调用对象
method.PushObject(obj);
//压入各参数
method.PushInteger(100);
} -
ref
和out
因为在一开始就压入了值,在这里需要压入他们的索引位置,后续需要通过传入的索引位置来获取值
ref
和out
参数 压入参数引用索引值即可 从0
开始1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20using (var method = appDomain.BeginInvoke(methodInfo))
{
//压入第一个ref参数的初始值
method.PushObject(list);
//压入第一个out参数的初始值,由于out参数不需要在外部初始化 所以压入null即可
method.PushObject(null);
//其他函数调用写法一致
//压入调用对象
method.PushObject(obj);
//压入各参数
method.PushInteger(100);
//ref和out因为在一开始就压入的值,因此在这里需要压入参数引用的索引值,从0开始即可
//这里有两个ref和out参数,因此执行两次该方法即可
method.PushReference(0);
method.PushReference(1);
//执行方法
method.Invoke();
} -
通过
Read
按顺序获取ref
/out
参数的值 和返回值,返回值最后获取
ref
和out
参数的获取需要传入索引值,索引值和PushReference
传入的索引值对应1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25using (var method = appDomain.BeginInvoke(methodInfo))
{
//压入第一个ref参数的初始值
method.PushObject(list);
//压入第一个out参数的初始值,由于out参数不需要在外部初始化 所以压入null即可
method.PushObject(null);
//其他函数调用写法一致
//压入调用对象
method.PushObject(obj);
//压入各参数
method.PushInteger(100);
//ref和out因为在一开始就压入的值,因此在这里需要压入参数引用的索引值,从0开始即可
//这里有两个ref和out参数,因此执行两次该方法即可
method.PushReference(0);
method.PushReference(1);
//执行方法
method.Invoke();
//通过Read按顺序获取ref/out参数的值 和返回值,返回值最后获取
list = method.ReadObject<List<int>>(0); //这里传入的索引值与PushReference传入的索引值对应
float f = method.ReadFloat(1); //这里传入的索引值与PushReference传入的索引值对应
float returnValue = method.ReadFloat(); //获取返回值,不需要传入索引
}
使用示例:
1 | void Start() |
输出: