I'm trying to pass a structure to a sub/void to fill it with data. In C# this works fine by doing
[TestFixture]
public class Boxing
{
[Test]
public void BoxingValue()
{
var res = (object)new Test();
SomeVoid(res);
Assert.AreEqual(2, ((Test)res).Id);
}
public static void SomeVoid(object b)
{
var f = b.GetType().GetField("Id");
f.SetValue(b, 2);
}
public struct Test
{
public int Id;
}
}
This code passes the test in C# in vb thoug
<Test> Public Sub StructTest()
Dim s As Object
s = CObj(New Test)
A(s)
Assert.AreEqual(2, CType(s, Test).Id)
End Sub
Public Sub A(val As Object)
Dim f = val.GetType().GetField("Id")
f.SetValue(val, 2)
End Sub
Public Structure Test
Public Id As Integer
End Structure
Does anyone have a explanation for this..
STRANGE?
I believe this is a known limitation with the use of SetValue
in VB when passing structures (even if the variable itself is declared as an Object
). If you look at the contents of val
within A
before and after the call to SetValue
, you'll see that it doesn't change the value of the struct. The explanation I've seen is that VB internally boxes the underlying value again (via a call to GetObjectValue
), creating a copy, and changes the copy's value.
One workaround I've seen is to cast the value to a ValueType
and the call SetValue
on it (you also need to change the parameter to be explicitly ByRef:
Public Sub A(ByRef val As Object)
Dim f = val.GetType().GetField("Id")
If val.GetType.IsValueType Then
Dim vt As ValueType = val
f.SetValue(vt, 2)
val = vt
Else
f.SetValue(val, 2)
End If
End Sub
Of course, this complexity just reinforces the principle that mutable structs should be avoided at all costs.
See Reflection on structure differs from class - but only in code for an explaination, but the summary is:
f.SetValue(val*, 2)
*{at this point val is being passed by value, ie it is a copy that is being updated}
as for a workaround...
'Struct workaround of course you only want this for structs!:
Public Sub A(ByRef val As Object)
Dim x As ValueType
x = CType(val, ValueType)
Dim f = x.GetType().GetField("Id")
f.SetValue(x, 2)
val = x
End Sub
Obviously you'll need to protect yourself to only run this for structs...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With