Click here to Skip to main content
15,512,747 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I'm attempting to use Reflection.FieldInfo.SetValue to modify a structure's integer field value. However, it doesn't get modified.

I realize, that SetValue expects an Object and that just giving it a value is working with a copy, but boxing the integer doesn't help either.

What is my mistake?

What I have tried:

Here's ready to copy and paste code in C# (further below in VB as well):

using System;
using System.Reflection;

public static class Test
{
    public struct SStruct
    {
        public int Value;
    }

    public static void Main()
    {
        // Initialize a structure record.
        SStruct rStruct = new SStruct();
        rStruct.Value = 42;

        // Reading the Value field by name:
        Type tStruct = typeof(SStruct);
        FieldInfo fValue = tStruct.GetField("Value");
        object oValue = fValue.GetValue(rStruct);
        Console.WriteLine("Read Value Before Mod: {0}", oValue);

        // Attempting to modify the Value field:
        fValue.SetValue(rStruct, 21);
        oValue = fValue.GetValue(rStruct);
        Console.WriteLine("Read Value After Mod:  {0}", oValue);
        // It didn't change.

        // SetValue is expecting an object though. Box the struct.
        object oStruct = rStruct;
        fValue.SetValue(oStruct, 21);
        oValue = fValue.GetValue(rStruct);
        Console.WriteLine("Read After Boxing:     {0}", oValue);
        // It didn't change.

        Console.Read();
    }
}


Here VB:

Imports System
Imports System.Reflection

Module Test
    Public Structure SStruct
        Public Value As Integer
    End Structure

    Public Sub Main()
        'Initialize a structure record.
        Dim rStruct As New SStruct
        rStruct.Value = 42

        'Reading the Value field by name:
        Dim tStruct As Type = GetType(SStruct)
        Dim fValue As FieldInfo = tStruct.GetField("Value")
        Dim oValue As Object = fValue.GetValue(rStruct)
        Console.WriteLine("Read Value Before Mod: {0}", oValue)

        'Attempting to modify the Value field:
        fValue.SetValue(rStruct, 21)
        oValue = fValue.GetValue(rStruct)
        Console.WriteLine("Read Value After Mod:  {0}", oValue)
        'It didn't change.

        'SetValue is expecting an object though. Box the struct.
        Dim oStruct As Object = rStruct
        fValue.SetValue(oStruct, 21)
        oValue = fValue.GetValue(rStruct)
        Console.WriteLine("Read After Boxing:     {0}", oValue)
        'It didn't change.

        Console.Read()
    End Sub
End Module


Edit:

Unboxing as suggested by Chris Copeland seems to do the trick in C#, but why does it not work in our VB enviroment? Check it out online on dotnetfiddle.net: C# produces 42,42,21 with this:

using System;
using System.Reflection;

public static class Test
{
    public struct SStruct
    {
        public int Value;
    }

    public static void Main()
    {
        // Initialize a structure record.
        SStruct rStruct = new SStruct();
        rStruct.Value = 42;

        // Reading the Value field by name:
        Type tStruct = typeof(SStruct);
        FieldInfo fValue = tStruct.GetField("Value");
        object oValue = fValue.GetValue(rStruct);
        Console.WriteLine("Read Value Before Mod: {0}", oValue);

        // Attempting to modify the Value field:
        fValue.SetValue(rStruct, 21);
        oValue = fValue.GetValue(rStruct);
        Console.WriteLine("Read Value After Mod:  {0}", oValue);
        // It didn't change.

        // SetValue is expecting an object though. Box the struct.
		object boxed = (object)rStruct;
		fValue.SetValue(boxed, 21);
		rStruct = (SStruct)boxed;
		
        oValue = fValue.GetValue(rStruct);
        Console.WriteLine("Read After Boxing:     {0}", oValue);
        // It didn't change.
   }
}


but 42,42,42 in VB:

Imports System
Imports System.Reflection

Public Module Test
    Public Structure SStruct
        Public Value As Integer
    End Structure

    Public Sub Main()
        'Initialize a structure record.
        Dim rStruct As New SStruct
        rStruct.Value = 42

        'Reading the Value field by name:
        Dim tStruct As Type = GetType(SStruct)
        Dim fValue As FieldInfo = tStruct.GetField("Value")
        Dim oValue As Object = fValue.GetValue(rStruct)
        Console.WriteLine("Read Value Before Mod: {0}", oValue)

        'Attempting to modify the Value field:
        fValue.SetValue(rStruct, 21)
        oValue = fValue.GetValue(rStruct)
        Console.WriteLine("Read Value After Mod:  {0}", oValue)
        'It didn't change.

        'SetValue is expecting an object though. Box the struct.
        Dim oStruct As Object = CType(rStruct, Object)
        fValue.SetValue(oStruct, 21)
        rStruct = CType(oStruct, SStruct)

        oValue = fValue.GetValue(rStruct)
        Console.WriteLine("Read After Boxing:     {0}", oValue)
        'It didn't change.
    End Sub
End Module
Posted
Updated 25-Feb-21 0:53am
v2
Comments
Maciej Los 25-Feb-21 5:17am    
Why do you want to change the value through the Reflection?
Visual Herbert 25-Feb-21 5:27am    
We have files with records of different structures. It is known which structure comes first, but this is the only one defining by its fields which structure types follow next. Reading the structures is all fine and doable, but unfortunately, values therein need to be modified sometimes. In order to avoid writing 4 bytes at file position 12134516 it would be much preferrable to simply modify the field "MyNumber" in the structure "MyStruct".

1 solution

A struct is a pass-by-value object, which means that anytime it gets passed into a method as an argument a new instance of the struct is created, and that will receive the updates. So when you call fValue.SetValue(rStruct, 21); what this is doing is creating a new instance of SStruct, and the field is modified in that.

A solution to this is to box the structure, and then unbox afterwards, like so:
C#
object boxed = (object)rStruct;
fValue.SetValue(boxed, 21);
rStruct = (SStruct)boxed;
 
Share this answer
 
Comments
Visual Herbert 25-Feb-21 5:23am    
But, isn't that exactly what I did? Please see the code block after fValue.SetValue(rStruct, 21);

It remains 42...
Chris Copeland 25-Feb-21 5:24am    
You need to unbox the struct again afterwards, as this contains the reference that was passed into the method. After calling SetValue() unbox the structure again and you should see the amended field.
Visual Herbert 25-Feb-21 5:41am    
Thanks Chris, it works – in C#. Output then is 42, 42, 21. Our environment is VB.Net though.

...
Dim oStruct As Object = CType(rStruct, Object)
fValue.SetValue(oStruct, 21)
rStruct = CType(oStruct, SStruct)
oValue = fValue.GetValue(rStruct)
Console.WriteLine("Read After Boxing: {0}", oValue)

still outputs 42,42,42.

I edited my question with copy-pastable source for online verification.
Chris Copeland 25-Feb-21 6:11am    
Just did a quick bit of research on this, apparently you need to use ValueType instead. So to get it working I changed Dim oStruct As Object = CType(rStruct, Object) to Dim oStruct As ValueType = rStruct and that seemed to work
Visual Herbert 25-Feb-21 6:55am    
Thanks kind sir, it works. I did waste my whole morning with research about this problem. Would you please be so kind to share your source?

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900