Search code examples
c#reflectionsystem.reflection

When using reflection, why aren't values being set correctly?


I'm trying to use reflection to create a struct

here is the code I'm using:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

public interface ISomeInterface
{

}

public struct SomeStruct : ISomeInterface
{
    public int Value;

    public SomeStruct(int value)
    {
        Value = value;
    }
}

public struct Filter
{
    public SomeStruct SomeStruct;
}

public class SomeClass
{
    private List<ISomeInterface> someInterfaces = new List<ISomeInterface>();

    public void Add(ISomeInterface someInterface)
    {
        this.someInterfaces.Add(someInterface);
    }

    public TFilter ToFilter<TFilter>() where TFilter : struct
    {
        var filterType = typeof(TFilter);
        var filterFields = filterType.GetFields();

        var result = Activator.CreateInstance<TFilter>();

        foreach (var filterField in filterFields)
        {
            var component = this.someInterfaces.First(s => s.GetType() == filterField.FieldType);

            filterField.SetValue(result, component);
        }

        return result;
    }
}

class Program
{
    public static void Main()
    {
        var someClass = new SomeClass();

        var someStruct = new SomeStruct(10);
        someClass.Add(someStruct);

        var filter = someClass.ToFilter<Filter>();

        // Should return 10, but returns 0, why?
        Console.WriteLine(filter.SomeStruct.Value);

        Console.ReadLine();
    }
}

For some reason, the result of the ToFilter method returns a struct with an incorrect value. I've been at this for hours and I just don't understand what's going on.

It's been frustrating me for hours, if someone could please help me that would be greatly appreciated.


Solution

  • The problem lies in this line:

    filterField.SetValue(result, component);
    

    SetValue method takes an object as it's first parameter. But the result is of type Filter which is a struct. This causes result to be boxed, and then inside SetValue the field of the boxed instance is being set. Which has no effect on the original result and it's value remains as zero.

    You should be careful when passing structs around because unexpected behaviours like this will always happen. You should not use structs like this if you are not aware of how boxing/unboxing works and the differences between value types and reference types.

    Here is a good place to start.