拳击在C#和VB中表现不同

时间:2014-10-28 15:58:59

标签: c# .net vb.net

我试图将结构传递给子/ void以用数据填充它。在C#中,通过执行

可以正常工作
[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;
    }
}

此代码在vb thoug

中通过C#中的测试
<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

有没有人对此有解释..

STRANGE吗

2 个答案:

答案 0 :(得分:7)

我认为这是一个已知的限制,在传递结构时在VB中使用SetValue(即使变量本身被声明为Object)。如果您在调用val之前和之后查看A内的SetValue内容,您会发现它不会更改结构的值。我看到的解释是VB在内部再次将基础值打包(通过调用GetObjectValue),创建副本,并更改副本的值

我看到的一个解决方法是将值转换为ValueType并在其上调用SetValue(您还需要将参数更改为显式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

当然,这种复杂性只是强化了应该不惜一切代价避免可变结构的原则。

答案 1 :(得分:2)

请参阅Reflection on structure differs from class - but only in code进行解释,但总结如下:

f.SetValue(val*, 2)

* {此时val正在按值传递,即它是正在更新的副本}

至于解决方法......

'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

显然你需要保护自己只为结构运行...