【问题标题】:Boxing behaves differently in C# and VB装箱在 C# 和 VB 中的行为不同
【发布时间】:2014-10-28 15:58:59
【问题描述】:

我正在尝试将结构传递给 sub/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

有人对此有解释吗..

奇怪吗?

【问题讨论】:

  • 我不明白。发布完整代码。 res的类型是什么?
  • 是的,如果没有C#中res的声明,就无法判断代码转换是否正确。您需要缺少的两行 - Dim s As Objects = CObj(New Test)
  • 能否在 C# 代码中也包含 &lt;Test&gt; Public Sub StructTest() 并确保所有类名和变量名一致? void a(b)A(val),还有 struct DtoStructStructure Test
  • 这段代码看得我眼花缭乱。可变结构...将 C# 转换为 VB...使用拳击的副作用...反射...嘎!
  • @PEtter:即便如此,实际受行为差异影响的实际代码仍然被破坏。至于解释,我认为与其在这里发布,最好的办法是查看 IL(请参阅 ildasm.exe,或 dotPeek 或 Reflector 之类的工具)并查看每个编译器生成的内容。我不知道为什么 VB.NET 会以不同的方式处理盒装结构,但答案在 IL 中。

标签: c# .net vb.net


【解决方案1】:

我相信这是在传递结构时在 VB 中使用 SetValue 的已知限制(即使变量本身被声明为 Object)。如果您在调用SetValue 之前和之后查看A 内的val 的内容,您会发现它不会改变结构的值。我看到的解释是,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。无论如何,这让我更加讨厌VB。每次对结构进行反思时,都必须对其进行复制。
  • @MillieSmith:只要遵循指南——结构应该是不可变的。在现实生活中,您永远不会对它们使用 SetValue。如果 VB 不允许你在真空中开发出球形马,那也不是讨厌它的好理由。
  • 我相信你在这里有所收获。我也让它工作做这个修改。但这不是真正的问题 :) 我相信 c# 和 vb 中的代码“相同”,但我编译为不同的 IL,这让我感到害怕。
  • 你的“问题”是要求解释——这就是解释。 VB 在内部将值 again 装箱并使用它的副本。
【解决方案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

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

【讨论】:

  • val 参数也需要传递“ByRef”,不是吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-01-07
  • 2011-09-19
  • 1970-01-01
  • 1970-01-01
  • 2022-06-16
  • 1970-01-01
  • 2013-08-25
相关资源
最近更新 更多