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
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".
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:
object boxed = ( object )rStruct; fValue.SetValue(boxed, 21 ); rStruct = (SStruct)boxed;
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.
Thank you.

The link provided there is dead, but it was captured by the Wayback Machine, if someone is interested:

https://web.archive.org/web/20170619134528/http://www.tech-archive.net/Archive/DotNet/microsoft.public.dotnet.languages.vb/2006-10/msg00219.html

The essence is:

Q: Why does the compiler emit a call to System.Runtime.CompilerServices.RuntimeHelpers.GetObjectValue whenever you pass an Object reference to an Object parameter?

A: The reason is because we do not want valuetype aliasing to occur. For example, assigning between Objects would copy only the _reference_ to a boxed valuetype. Modifying what the user thinks is a unique instance would in fact modify both copies, which we thought would be surprising and bad. Using reflection and CallByName isn't impossible because you can use System.ValueType. Assigning or passing System.ValueType's will NOT generate calls to GetObjectValue. In other words, you can force aliasing by using System.ValueType instead of Object to manipulate your valuetypes."
  • Read the question carefully.
  • Understand that English isn't everyone's first language so be lenient of bad spelling and grammar.
  • If a question is poorly phrased then either ask for clarification, ignore it, or edit the question and fix the problem. Insults are not welcome.
  • Don't tell someone to read the manual. Chances are they have and don't get it. Provide an answer or move on to the next question. Let's work to help developers, not make them feel stupid.
  •