You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 07:40:22