TL;DR: In VB.NET you need to use System.ValueType
when working with boxed mutable structs而不是System.Object
(在VB.NET中也称为Object
,在C#中也称为object
).
将您的代码更改为以下代码,它就可以工作了(至少对我来说,在LinqPad 7中面向.NET 7的VB.NET中是这样):
(您可能希望将.Dump()
个呼叫替换为Console.WriteLine
或类似的呼叫)
Dim heightProperty = GetType( Size ).GetProperty( "Height", BindingFlags.Public Or BindingFlags.Instance )
Dim localStruct As Size
localStruct.Width = 5
localStruct.Dump( "Should have Width 5" )
' This `SetValue` call below won't work because `vbc` will insert a hidden `PropertyInfo.SetValue(Object, Object)` call which will box `localStruct` such that you can't retrieve it after it's mutated, so effectively discarding it.
heightProperty.SetValue( localStruct, 10 )
localStruct.Dump( ( "Should have Height 10, but has " + localStruct.ToString() ) )
Dim boxedAsObject As System.Object = localStruct
LINQPad.Extensions.Dump( boxedAsObject, "boxed" ) ' interestingly, you can't use extension-method syntax on Object-typed boxed structs, weird.
heightProperty.SetValue( boxedAsObject, 10) ' This won't work because `vbc` will insert a hidden `PropertyInfo.SetValue(Object, Object)` call which will reset `boxedAsObject`.
LINQPad.Extensions.Dump( boxedAsObject, ( "Should have Height 10, but has " + boxedAsObject.ToString() ) )
' https://www.pcreview.co.uk/threads/cannot-get-propertyinfo-setvalue-to-work-for-a-structure.1418755/
Dim boxedAsValueType As System.ValueType = localStruct
heightProperty.SetValue( boxedAsValueType, 10 )
boxedAsValueType.Dump( "Will have Height = 10: " + boxedAsValueType.ToString() )
给出了以下输出:
与C#相比,VB.NET有自己的特殊规则来处理盒装 struct :当您在VB.NET中将System.Object
用作盒装 struct 的_QUIY_TOP类型时,IL编译器将插入对RuntimeHelpers.GetObjectValue
的隐藏调用,这将导致堆栈上的盒装 struct 引用被替换为new盒装 struct 实例.
(下面的IL是在启用优化的情况下编译的)
IL_0000 ldtoken Size
IL_0005 call Type.GetTypeFromHandle (RuntimeTypeHandle)
IL_000A ldstr "Height"
IL_000F ldc.i4.s 14 // 20
IL_0011 call Type.GetProperty (String, BindingFlags)
IL_0016 ldloca.s 00 // localStruct
IL_0018 ldc.i4.5
IL_0019 call Size.set_Width (Int32)
IL_001E ldloc.0 // localStruct
IL_001F ldstr "Should have Width 5"
IL_0024 call Extensions.Dump<Size> (Size, String)
IL_0029 pop
IL_002A dup
IL_002B ldloc.0 // localStruct
IL_002C box Size
IL_0031 ldc.i4.s 0A // 10
IL_0033 box Int32
IL_0038 callvirt PropertyInfo.SetValue (Object, Object)
IL_003D ldloc.0 // localStruct
IL_003E ldstr "Should have Height 10, but has "
IL_0043 ldloca.s 00 // localStruct
IL_0045 constrained. Size
IL_004B callvirt Object.ToString ()
IL_0050 call String.Concat (String, String)
IL_0055 call Extensions.Dump<Size> (Size, String)
IL_005A pop
IL_005B ldloc.0 // localStruct
IL_005C box Size
IL_0061 stloc.1 // boxedAsObject
IL_0062 ldloc.1 // boxedAsObject
IL_0063 call RuntimeHelpers.GetObjectValue (Object)
IL_0068 ldstr "boxed"
IL_006D call Extensions.Dump<Object> (Object, String)
IL_0072 pop
IL_0073 dup
IL_0074 ldloc.1 // boxedAsObject
IL_0075 call RuntimeHelpers.GetObjectValue (Object)
IL_007A ldc.i4.s 0A // 10
IL_007C box Int32
IL_0081 callvirt PropertyInfo.SetValue (Object, Object)
IL_0086 ldloc.1 // boxedAsObject
IL_0087 call RuntimeHelpers.GetObjectValue (Object)
IL_008C ldstr "Should have Height 10, but has "
IL_0091 ldloc.1 // boxedAsObject
IL_0092 callvirt Object.ToString ()
IL_0097 call String.Concat (String, String)
IL_009C call Extensions.Dump<Object> (Object, String)
IL_00A1 pop
IL_00A2 ldloc.0 // localStruct
IL_00A3 box Size
IL_00A8 stloc.2 // boxedAsValueType
IL_00A9 ldloc.2 // boxedAsValueType
IL_00AA ldc.i4.s 0A // 10
IL_00AC box Int32
IL_00B1 callvirt PropertyInfo.SetValue (Object, Object)
IL_00B6 ldloc.2 // boxedAsValueType
IL_00B7 ldstr "Will have Height = 10: "
IL_00BC ldloc.2 // boxedAsValueType
IL_00BD callvirt ValueType.ToString ()
IL_00C2 call String.Concat (String, String)
IL_00C7 call Extensions.Dump<ValueType> (ValueType, String)
IL_00CC pop
IL_00CD ret
...而等效C#的IL缺少这些隐藏调用:
IL_0000 ldtoken Size
IL_0005 call Type.GetTypeFromHandle (RuntimeTypeHandle)
IL_000A ldstr "Height"
IL_000F ldc.i4.s 14 // 20
IL_0011 call Type.GetProperty (String, BindingFlags)
IL_0016 ldloca.s 00 // localStruct
IL_0018 initobj Size
IL_001E ldloca.s 00 // localStruct
IL_0020 ldc.i4.5
IL_0021 call Size.set_Width (Int32)
IL_0026 ldloc.0 // localStruct
IL_0027 ldstr "Should have Width 5"
IL_002C call Extensions.Dump<Size> (Size, String)
IL_0031 pop
IL_0032 dup
IL_0033 ldloc.0 // localStruct
IL_0034 box Size
IL_0039 ldc.i4.s 0A // 10
IL_003B box Int32
IL_0040 callvirt PropertyInfo.SetValue (Object, Object)
IL_0045 ldloc.0 // localStruct
IL_0046 ldstr "Should have Height 10"
IL_004B call Extensions.Dump<Size> (Size, String)
IL_0050 pop
IL_0051 ldloc.0 // localStruct
IL_0052 box Size
IL_0057 stloc.1 // boxedAsObject
IL_0058 dup
IL_0059 ldloc.1 // boxedAsObject
IL_005A ldc.i4.s 0A // 10
IL_005C box Int32
IL_0061 callvirt PropertyInfo.SetValue (Object, Object)
IL_0066 ldloc.1 // boxedAsObject
IL_0067 ldstr "boxedAsObject"
IL_006C call Extensions.Dump<Object> (Object, String)
IL_0071 pop
IL_0072 ldloc.0 // localStruct
IL_0073 box Size
IL_0078 stloc.2 // boxedAsValueType
IL_0079 ldloc.2 // boxedAsValueType
IL_007A ldc.i4.s 0A // 10
IL_007C box Int32
IL_0081 callvirt PropertyInfo.SetValue (Object, Object)
IL_0086 ldloc.2 // boxedAsValueType
IL_0087 ldstr "boxedAsValueType"
IL_008C call Extensions.Dump<ValueType> (ValueType, String)
IL_0091 pop
IL_0092 ret