【出现异常】异常信息为:{"ClassName":"System.FormatException","Message":"输入字符串的格式不正确。。。。。。

本文记录了一个关于序列化过程中出现“输入字符串的格式不正确”错误的问题排查过程。通过对比使用‘+’操作符与占位符的方式,最终定位并解决了问题。

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

今天来说说排查问题时遇到的一个问题吧:

问题就是报了如下错误:

输入字符串的格式不正确。

找了半天,我以为是json序列化和反序列化的原因,然后,并不是:


日志是这么记的,但是呢,看日志的时候,发现,值记到了,前面的请求参数,后面就加上了异常信息的内容,所以就判断在序列化那一步,但是在代码上,上面一部分,序列化已经有过了,并没有报错,所以就猜想,是‘+’的原因,所以就尝试了一波,改成占位符。

这个是用的StringBuilder.AppendFormat方法,用+号呢,我是不怎么用,我习惯使用占位符,如下:


然后这个问题就解决了。

<think>好的,我现在需要帮助用户理解他们得到的输出“Static Field Value: System.Collections.Generic.HashSet`1[System.String]”。首先,用户之前询问了如何使用frida-il2cpp-bridge打印字段,现在遇到了一个具体的输出结果,看起来他们可能太明白这个结果的含义。 首先,我要确认用户的输出是什么。他们使用frida-il2cpp-bridge打印了一个静态字段的值,得到的类型是System.Collections.Generic.HashSet`1[System.String]。这里的`1表示这是一个泛型类型,而[System.String]是类型参数。所以这个字段实际上是一个存储字符串的HashSet集合。 接下来,我需要考虑用户为什么会得到这个结果。可能他们在尝试访问一个静态字段,但直接使用toString()方法时,返回的是类型信息而是具体内容。这可能让用户感到困惑,因为他们可能期望看到集合中的实际字符串元素。 用户可能的需求是想要获取这个HashSet中的具体数据,而仅仅是类型信息。因此,我需要解释为什么直接打印会显示类型,以及如何进一步遍历集合中的元素。同时,用户可能对泛型类型在IL2CPP中的表示熟悉,需要解释`1的含义和如何处理泛型类型。 另外,用户可能在使用frida-il2cpp-bridge时遇到了技术障碍,比如如何正确访问泛型集合的元素。需要提供具体的代码示例,展示如何将字段值转换为Il2Cpp.Object,然后访问其内部方法和字段,比如获取Count和遍历元素。 还需要提醒用户注意的事项,比如确保游戏已经初始化了该静态字段,否则可能获取到空引用。此外,处理泛型类型时可能需要使用Il2Cpp.corlib,特别是System.Collections.Generic命名空间下的类。 总结来说,用户的深层需求可能仅仅是理解输出结果,而是想要进一步操作和查看集合中的具体数据。因此,我的回答需要分步骤解释现象原因,并提供代码示例和注意事项,帮助他们实现目标。</think>--- ### **现象解释** 你看到的 `System.Collections.Generic.HashSet`1[System.String]` 表示这是一个 **C# 泛型集合类型**,具体来说是一个存储字符串的 `HashSet<string>`。这里的 `1` 是泛型参数占位符,`[System.String]` 表示泛型类型参数为 `string`。 --- ### **为什么会出现这种输出?** 1. **默认 `toString()` 行为** IL2Cpp 对象直接调用 `.toString()` 时,默认返回类型名称(而是集合内容)。 类似于 C# 中直接打印 `HashSet<string>` 对象的默认行为:`Console.WriteLine(myHashSet); // 输出类型名称` 2. **泛型类型在 IL2CPP 中的表示** IL2CPP 会将泛型类型转换为 `ClassName`T 格式,例如: `HashSet<string>` → `System.Collections.Generic.HashSet`1[System.String]` --- ### **如何打印 HashSet 具体内容?** 你需要手动遍历集合元素。以下是完整代码示例: ```javascript const { Il2Cpp } = require('frida-il2cpp-bridge'); Il2Cpp.initialize().then(async () => { // 1. 获取目标类(假设类名为 GameManager) const GameManager = Il2Cpp.Domain.assembly('Assembly-CSharp').image.class['Namespace.GameManager']; // 2. 获取静态 HashSet 字段 const hashSetField = GameManager.field('stringHashSet').value; // 3. 转换为 Il2Cpp.Object 并验证类型 if (hashSetField instanceof Il2Cpp.Object) { const hashSet = hashSetField as Il2Cpp.Object; // 4. 获取集合元素数量 const count = hashSet.method('get_Count').invoke().toInt32(); console.log(`HashSet 包含 ${count} 个元素`); // 5. 遍历元素(需通过 Enumerator) const enumerator = hashSet.method('GetEnumerator').invoke(); while (enumerator.method('MoveNext').invoke().toBoolean()) { const current = enumerator.method('get_Current').invoke().toString(); console.log('元素:', current); } } else { console.log('字段是对象类型'); } }); ``` --- ### **关键注意事项** 1. **泛型方法的特殊处理** 如果字段是 `HashSet<T>` 且 `T` 是自定义类型,需使用 `Il2Cpp.corlib` 中的泛型方法: ```javascript const systemCollections = Il2Cpp.corlib.assembly('System.Collections').image; const hashSetType = systemCollections.class['System.Collections.Generic.HashSet`1']; ``` 2. **静态字段初始化时机** 确保游戏已初始化该静态字段(例如通过进入某个场景或调用初始化方法),否则可能得到空引用。 3. **性能优化** 高频遍历集合时建议缓存 `get_Count`、`GetEnumerator` 等方法对象,避免重复查找。 --- ### **扩展应用** - **修改 HashSet 内容** 通过 Hook `Add`/`Remove` 方法监控或修改集合: ```javascript const addMethod = hashSetType.method('Add'); addMethod.intercept((args) => { const newItem = args[0].toString(); console.log('正在添加元素:', newItem); return addMethod.invoke(args); // 允许原逻辑执行 }); ``` - **直接内存访问(高级)** 若官方 API 限制较多,可通过指针直接读取集合内部数组: ```javascript const internalArrayPtr = hashSet.field('_slots').value; // 内部结构取决于 Unity 版本 ``` --- ### **总结** - 输出 `HashSet`1[String]` 是泛型类型的标准表示形式。 - 要获取实际内容,需通过 `GetEnumerator()` 遍历。 - 注意处理 IL2CPP 与原生 C# 在泛型实现上的差异。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值