Search code examples
c#unity-game-enginereflection

C# - Field values set with reflection aren't updating


I'm making a statistics system for a Unity game and attempting to write some functions that update a given statistic (identified by their field name) with a given value using reflection. I know reflection can be controversial but I thought it would be better practice than exposing the fields containing the statistics structs. The only issue is that the field values don't seem to be actually updating -- printing the before and after field values shows that they remain the same even after these functions are called.

Here's what I've tried for now. _lifetimeStats is a private static struct which contains various fields holding different statistics. The int is being used on fields which are already defined as ints.

        private static LifetimeStatistics _lifetimeStats;
        
        /// <summary>
        /// Replaces the current value of the statistic with the provided value.
        /// See also: <see cref="UpdateLifetimeStat"/>
        /// </summary>
        public static void SetLifetimeStat(string statName, object value)
        {
            var stat = _lifetimeStats.GetType().GetField(statName);
            stat.SetValue(_lifetimeStats, value);
        }

        /// <summary>
        /// Adds the provided value to the current value of the statistic.
        /// See also: <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);
        }

And here's a snippet of the LifetimeStats struct:


public struct LifetimeStatistics {
        public int TotalMinutesPlayed; 
        public int TotalNumberOfJumps; 
        public int TotalNumberOfDeaths; 
        public int TotalDistanceTraveled;
        public int CurrentDailyPlayStreak; 
         ... other fields, constructors, etc...
}

Anyone have an idea of why the field values aren't updating?


Solution

  • This is because LifetimeStatistics is a struct and is being boxed. Try this:

    public static void SetLifetimeStat(string statName, object value)
    {
        //without SetValueDirect (older versions of Unity don't support SetValueDirect)
        var stat = _lifetimeStats.GetType().GetField(statName);
        object copy = _lifetimeStats;
        stat.SetValue(copy, value);
        _lifetimeStats = (LifetimeStatistics)copy;
    
        //with SetValueDirect
        var stat = _lifetimeStats.GetType().GetField(statName);
        stat.SetValueDirect(__makeref(_lifetimeStats), value);
    }
    

    However I don't think you should be using reflection here. Reflection is slow, and in this case you have no way of knowing what stats are available for the user calling the functions besides also possibly not using the right type for value. Ignoring the issue of mutable structs, something without reflection which essentially does the same thing would be to use enums like so:

    void SetStat(StatEnum enum, object value)
    {
        //use a switch and set corresponding value
    }
    

    However I would also not recommend this. You still have the same issue of not knowing if you have the correct type for value.

    If you only need to get the value outside of the struct you can also make them properties and give them a private set but public get.

    Think of what you're trying to gain by not directly accessing the fields that you want to be changed and accessed from outside.