Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Boxing behaves differently in C# and VB

Tags:

c#

.net

vb.net

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?

like image 331
PEtter Avatar asked Oct 28 '14 15:10

PEtter


2 Answers

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.

like image 173
D Stanley Avatar answered Sep 30 '22 16:09

D Stanley


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...

like image 26
tolanj Avatar answered Sep 30 '22 18:09

tolanj