2014-10-28 14 views
8

Próbuję przekazać strukturę do sub/void, aby wypełnić dane. W języku C# to działa prawidłowo, wykonującBoks zachowuje się inaczej w C# i 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; 
    } 
} 

Ten kod przechodzi test w języku C# w 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 

Czy ktoś ma wytłumaczenia dla tego ..

dziwne?

+4

Nie rozumiem tego. Opublikuj pełny kod. Jaki jest typ 'res'? –

+0

Tak, bez deklaracji 'res' w C#, niemożliwe jest określenie, czy kod został poprawnie przekonwertowany. Potrzebujesz dwóch brakujących linii - 'Dim s As Object' i' s = CObj (New Test) '. – Neolisk

+0

mój zły .. powinien być tam teraz – PEtter

Odpowiedz

7

Wierzę, że jest to znane ograniczenie z użyciem SetValue w VB podczas przekazywania struktur (nawet jeśli sama zmienna jest zadeklarowana jako Object). Jeśli spojrzysz na zawartość val w ciągu A przed i po wywołaniu SetValue, zobaczysz, że nie zmienia wartości struktury. Wyjaśnienie, które widziałem, polega na tym, że VB wewnętrznie zapisuje wartość bazową ponownie (przez połączenie z numerem GetObjectValue), tworząc kopię i zmienia jej wartość.

Jeden obejście widziałem jest do oddania wartości do ValueType i wezwanie SetValue na nim (trzeba także zmienić parametr być wyraźnie 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 

Oczywiście, to tylko złożoność wzmacnia zasadę, że Zmienne elemencie należy unikać za wszelką cenę

+0

Bez problemu. Daje ci +1. W każdym razie sprawia to, że nienawidzę VB jeszcze bardziej. Musisz zrobić kopię struktury za każdym razem, gdy będziesz się nad nią zastanawiać. –

+0

@MillieSmith: Postępuj zgodnie z wytycznymi - struktury mają być niezmienne. Nigdy nie używasz SetValue na nich w prawdziwym życiu. Jeśli VB nie pozwoli ci rozwinąć sferycznego konia w próżni, nie jest dobrym powodem, aby go nienawidzić. – Neolisk

+0

Wierzę, że coś tu jest. Zrobiłem też pracę, wykonując te modyfikacje. Ale to nie było właściwe pytanie :) Wierzę, że kod "jest taki sam" w języku C# i vb, ale kompiluję do różnych IL i to mnie przeraża. – PEtter

2

Zobacz Reflection on structure differs from class - but only in code dla wyjaśnieniu, ale podsumowanie jest:.

f.SetValue(val*, 2) 

* {W tym momencie val jest przekazywane przez wartość, to znaczy, że jest to kopia, która jest aktualizowana}

jak na obejście ...

'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 

Oczywiście trzeba chronić siebie aby uruchomić to tylko dla struktur ...

+1

Parametr val również musiałby zostać przekazany "ByRef", czyż nie? – dodald