2014-10-28 32 views
8

我試圖將結構傳遞給子/空來填充數據。在C#這工作做拳擊行爲在C#和VB中有所不同

[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 

有沒有人有這樣的解釋..

奇怪嗎?

+4

我不明白。發佈完整的代碼。什麼是'res'的類型? – 2014-10-28 16:03:20

+0

是的,沒有在C#中聲明'res',就無法確定代碼是否被正確轉換。您需要兩條缺失的行 - 「Dim s As Object」和「s = CObj(New Test)」。 – Neolisk 2014-10-28 16:04:10

+0

我的壞..現在應該在那裏 – PEtter 2014-10-28 16:05:21

回答

7

我相信這是一個已知的侷限性,即在傳遞結構時(即使變量本身被聲明爲Object),在VB中使用SetValue。如果您在調用SetValue之前和之後查看val的內容A,則會看到它不會更改結構的值。我已經看到的解釋是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 

當然,這只是複雜強化了可變的結構應該不惜一切代價避免的原則

+0

沒問題。給你一個+1。無論如何,這讓我更討厭VB。每次對它進行反思時,你都必須製作一份結構的副本。 – 2014-10-28 17:26:19

+0

@MillieSmith:只要遵循指導原則 - 結構應該是不可變的。在現實生活中,你從不在他們身上使用SetValue。如果VB不讓你在真空中開發一匹球形的馬,那麼討厭它不是一個好理由。 – Neolisk 2014-10-28 17:36:19

+0

我相信你在這裏。我也做了這個修改工作。但這不是真正的問題:)我相信代碼'是相同的'在C#中的VB,但我編譯爲不同的IL,這是什麼讓我害怕。 – PEtter 2014-10-28 17:40:34

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 

顯然,你需要保護自己只能運行這個結構...

+1

val參數也需要傳遞「ByRef」,不是嗎? – dodald 2014-10-28 16:58:28