ILRuntime第三课Delegate

本文介绍如何在Unity环境中使用ILRuntime实现热更新,并详细解释了如何注册和使用自定义委托,包括委托适配器及转换器的具体实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

​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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using ILRuntime.Runtime.Enviorment;
using System.IO;
using ILRuntime.CLR.Method;
 
public delegate void TestDelegateMethod(int a);
public delegate string TestDelegateFunction(int a);
 
public class DelegateDemo : MonoBehaviour {
    public static TestDelegateMethod TestMethodDelegate;
    public static TestDelegateFunction TestFunctionDelegate;
    public static System.Action<string> TestActionDelegate;
 
    //AppDomain是ILRuntime的入口,最好是在一个单例类中保存,整个游戏全局就一个,这里为了示例方便,每个例子里面都单独做了一个
    //大家在正式项目中请全局只创建一个AppDomain
    AppDomain appdomain;
 
    void Start()
    {
        StartCoroutine(LoadHotFixAssembly());
    }
 
    IEnumerator LoadHotFixAssembly()
    {
        //首先实例化ILRuntime的AppDomain,AppDomain是一个应用程序域,每个AppDomain都是一个独立的沙盒
        appdomain = new ILRuntime.Runtime.Enviorment.AppDomain();
        //正常项目中应该是自行从其他地方下载dll,或者打包在AssetBundle中读取,平时开发以及为了演示方便直接从StreammingAssets中读取,
        //正式发布的时候需要大家自行从其他地方读取dll
 
        //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        //这个DLL文件是直接编译HotFix_Project.sln生成的,已经在项目中设置好输出目录为StreamingAssets,在VS里直接编译即可生成到对应目录,无需手动拷贝
#if UNITY_ANDROID
        WWW www = new WWW(Application.streamingAssetsPath + "/HotFix_TestProject.dll");
#else
        WWW www = new WWW("file:///" + Application.streamingAssetsPath + "/HotFix_TestProject.dll");
#endif
        while (!www.isDone)
            yield return null;
        if (!string.IsNullOrEmpty(www.error))
            UnityEngine.Debug.LogError(www.error);
        byte[] dll = www.bytes;
        www.Dispose();
 
        //PDB文件是调试数据库,如需要在日志中显示报错的行号,则必须提供PDB文件,不过由于会额外耗用内存,正式发布时请将PDB去掉,下面LoadAssembly的时候pdb传null即可
#if UNITY_ANDROID
        www = new WWW(Application.streamingAssetsPath + "/HotFix_TestProject.pdb");
#else
        www = new WWW("file:///" + Application.streamingAssetsPath + "/HotFix_TestProject.pdb");
#endif
        while (!www.isDone)
            yield return null;
        if (!string.IsNullOrEmpty(www.error))
            UnityEngine.Debug.LogError(www.error);
        byte[] pdb = www.bytes;
        using (System.IO.MemoryStream fs = new MemoryStream(dll))
        {
            using (System.IO.MemoryStream p = new MemoryStream(pdb))
            {
                appdomain.LoadAssembly(fs, p, new Mono.Cecil.Pdb.PdbReaderProvider());
            }
        }
 
        InitializeILRuntime();
        OnHotFixLoaded();
    }
 
    void InitializeILRuntime()
    {
        //下面这些注册代码,正式使用的时候,应该写在InitializeILRuntime中
        //TestDelegateMethod, 这个委托类型为有个参数为int的方法,注册仅需要注册不同的参数搭配即可
        appdomain.DelegateManager.RegisterMethodDelegate<int>();
        //带返回值的委托的话需要用RegisterFunctionDelegate,返回类型为最后一个
        appdomain.DelegateManager.RegisterFunctionDelegate<intstring>();
        //Action<string> 的参数为一个string
        appdomain.DelegateManager.RegisterMethodDelegate<string>();
 
        //TestDelegateMethod委托转换器
        appdomain.DelegateManager.RegisterDelegateConvertor<TestDelegateMethod>((act) =>
        {
            return new TestDelegateMethod((a) =>
            {
                ((System.Action<int>)act)(a);
            });
        });
 
        appdomain.DelegateManager.RegisterDelegateConvertor<TestDelegateFunction>((act) =>
        {
            return new TestDelegateFunction((a) =>
            {
                return ((System.Func<int,string>)act)(a);
            });
        });
 
 
    }
 
    void OnHotFixLoaded() {
        Debug.Log("完全在热更DLL内部使用的委托,直接可用,不需要做任何处理");
        appdomain.Invoke("HotFix_TestProject.DelegateTest""Initialize",null,null);
        appdomain.Invoke("HotFix_TestProject.DelegateTest""RunTest"null,null);
 
        Debug.Log("如果需要跨域调用委托(将热更DLL里面的委托实例传到Unity主工程用), 就需要注册适配器,不然就会像下面这样");
 
        try {
            //适配器错误会消失,因为在InitializeILRuntime中写了委托适配器,但是会报转换器的错误
            appdomain.Invoke("HotFix_TestProject.DelegateTest""Initialize2"nullnull);
            appdomain.Invoke("HotFix_TestProject.DelegateTest""RunTest2"nullnull);
        }
        catch (System.Exception ex) {
            Debug.LogError(ex.ToString());
        }
 
        //为了演示,清除适配器缓存,实际使用中不要这么做
        //ClearDelegateCache();
        //Debug.Log("这是因为iOS的IL2CPP模式下,不能动态生成类型,为了避免出现不可预知的问题,我们没有通过反射的方式创建委托实例,因此需要手动进行一些注册");
        //Debug.Log("首先需要注册委托适配器,刚刚的报错的错误提示中,有提示需要的注册代码");
        //Debug.Log("ILRuntime内部是用Action和Func这两个系统内置的委托类型来创建实例的,所以其他的委托类型都需要写转换器");
        //Debug.Log("将Action或者Func转换成目标委托类型");
 
        Debug.Log("我们再来在Unity主工程中调用一下刚刚的委托试试");
        TestMethodDelegate(789);
        var str = TestFunctionDelegate(098);
        Debug.Log("!! OnHotFixLoaded str = " + str);
        TestActionDelegate("Hello From Unity Main Project");
 
    }
 
    void ClearDelegateCache() {
        var type = appdomain.LoadedTypes["HotFix_TestProject.DelegateTest"];
        ILMethod m = type.GetMethod("Method", 1) as ILMethod;
        m.DelegateAdapter = null;
 
        m = type.GetMethod("Function", 1) as ILMethod;
        m.DelegateAdapter = null;
 
        m = type.GetMethod("Action", 1) as ILMethod;
        m.DelegateAdapter = null;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值