Unity游戏统计系统:反射更新结构体字段值不生效问题
问题
我正在为Unity游戏开发一套统计系统,尝试用反射编写函数,根据字段名更新指定统计数据的值。我知道反射有争议,但觉得这比直接暴露统计结构体的字段更规范。现在遇到的问题是:调用相关函数后,字段值根本没更新——打印调用前后的字段值显示完全没变。
以下是我的实现代码:_lifetimeStats是私有静态结构体,包含各类统计字段,int值对应已定义为int类型的字段。
private static LifetimeStatistics _lifetimeStats; /// <summary> /// 用传入的值替换当前统计项的值。 /// 另见:<see cref="UpdateLifetimeStat"/> /// </summary> public static void SetLifetimeStat(string statName, object value) { var stat = _lifetimeStats.GetType().GetField(statName); stat.SetValue(_lifetimeStats, value); } /// <summary> /// 将传入的值加到当前统计项的值上。 /// 另见:<see cref="SetLifetimeStat"/> /// </summary> public static void UpdateLifetimeStat(string statName, int value) { var stat = _lifetimeStats.GetType().GetField(statName); stat.SetValue(_lifetimeStats, (int)stat.GetValue(_lifetimeStats) + value); }
LifetimeStatistics结构体片段:
public struct LifetimeStatistics { public int TotalMinutesPlayed; public int TotalNumberOfJumps; public int TotalNumberOfDeaths; public int TotalDistanceTraveled; public int CurrentDailyPlayStreak; // ... 其他字段、构造函数等 }
有人知道字段值没更新的原因吗?
解决方案
核心问题在于结构体是值类型:当你调用stat.SetValue(_lifetimeStats, value)时,_lifetimeStats作为值类型会被复制一份,你实际修改的是这个临时副本的字段值,而不是静态变量_lifetimeStats本身。修改完成后副本被丢弃,原变量自然没有变化。
你可以通过两种方式解决:
1. 将结构体改为类(推荐方案)
类是引用类型,反射操作时会直接作用于原对象,无需额外处理。如果没有必须使用结构体的业务或性能要求,这是最简单高效的解决办法:
public class LifetimeStatistics { public int TotalMinutesPlayed; public int TotalNumberOfJumps; public int TotalNumberOfDeaths; public int TotalDistanceTraveled; public int CurrentDailyPlayStreak; // ... 其他字段、构造函数等 }
2. 保留结构体,修改反射逻辑
如果一定要使用结构体,需要先获取当前静态实例,修改后再将修改后的实例赋值回静态变量:
private static LifetimeStatistics _lifetimeStats; public static void SetLifetimeStat(string statName, object value) { var field = typeof(LifetimeStatistics).GetField(statName); // 获取当前静态实例的副本 var currentStats = _lifetimeStats; // 修改副本的字段值 field.SetValue(currentStats, value); // 将修改后的副本赋值回静态变量 _lifetimeStats = currentStats; } public static void UpdateLifetimeStat(string statName, int value) { var field = typeof(LifetimeStatistics).GetField(statName); var currentStats = _lifetimeStats; var currentValue = (int)field.GetValue(currentStats); field.SetValue(currentStats, currentValue + value); _lifetimeStats = currentStats; }
这里用typeof(LifetimeStatistics)替代_lifetimeStats.GetType(),避免了不必要的实例调用,性能更优。
额外优化建议
反射的性能开销不可忽视,如果这些统计更新函数会被频繁调用,建议提前缓存FieldInfo对象,比如用一个字典存储字段名与对应的FieldInfo,避免每次调用都重新反射获取:
private static readonly Dictionary<string, FieldInfo> _statFields = new Dictionary<string, FieldInfo>(); static YourStatsClass() { var fields = typeof(LifetimeStatistics).GetFields(); foreach (var field in fields) { _statFields[field.Name] = field; } } // 之后在Set/Update函数中直接从_statFields获取FieldInfo
内容的提问来源于stack exchange,提问作者TwelvePointFive
相关产品推荐
相关产品推荐

