以下代码似乎不起作用,尽管根据文档,带有装箱的部分应该可以起作用.我似乎找不到比Value Type Property setting的文章更多的关于这方面的信息

这道题也不同于Setting a property of a property in a struct?题,因为这道题与我正在try 使用的反思无关.

这些代码只是一个大得多的项目的一小部分(使用反射来完成一些事情),但演示了这个问题.

本例的特殊之处在于,我们要设置的属性是值类型( struct ),而SetValue似乎没有很好地处理这一问题……

Imports System.Drawing

Module Module1
    Sub Main()
        Console.WriteLine("Starting test")
        Dim s As Size
        Console.WriteLine("Created size: {0}", s)
        s.Width = 5
        Console.WriteLine("Set Width: {0}", s)
        Dim pi = s.GetType().GetProperty("Height")
        Console.WriteLine("get property info: {0}", pi)
        pi.SetValue(s, 10, Nothing)
        Console.WriteLine("setting height with pi: {0}", s)
        Dim box As Object = s 'same result with  RuntimeHelpers.GetObjectValue(s)
        Console.WriteLine("boxing: {0}", box)
        pi.SetValue(box, 10, Nothing)
        Console.WriteLine("setting value: {0}", box)
        Console.WriteLine("")
        Console.WriteLine("End test")
        Console.ReadLine()
    End Sub
End Module

给出的结果是:

Starting test
Created size: {Width=0, Height=0}
Set Width: {Width=5, Height=0}
get property info: Int32 Height
setting height with pi: {Width=5, Height=0} 'expected 5,10
boxing: {Width=5, Height=0}
setting value: {Width=5, Height=0} 'expected 5,10

End test

同样的代码在C#中也可以(使用装箱)工作.

    internal class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Starting test");
            Size s= new Size();
            Console.WriteLine("Created size: {0}", s);
            s.Width = 5;
            Console.WriteLine("Set Width: {0}", s);
            PropertyInfo pi = s.GetType().GetProperty("Height");
            Console.WriteLine("get property info: {0}", pi);
            Console.WriteLine("can write?: {0}", pi.CanWrite);
            pi.SetValue(s, 10, null);
            Console.WriteLine("setting height with pi: {0}", s);
            Object box = s; //same result with  RuntimeHelpers.GetObjectValue(s)
            Console.WriteLine("boxing: {0}", box);
            pi.SetValue(box, 10, null);
            Console.WriteLine("setting value: {0}", box);
            Console.WriteLine("");
            Console.WriteLine("End test");
            Console.ReadLine();
        }
    }
Starting test
Created size: {Width=0, Height=0}
Set Width: {Width=5, Height=0}
get property info: Int32 Height
can write?: True
setting height with pi: {Width=5, Height=0}
boxing: {Width=5, Height=0}
setting value: {Width=5, Height=10}

End test

如何在VB中实现这一点?

推荐答案

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() )

给出了以下输出:

enter image description here


与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

.net相关问答推荐

API响应返回null错误. NET MAUI

从容器化客户端应用程序连接到 OPC-UA 服务器

WinForm Task.Wait :为什么它会阻塞 UI?

Puppeteer LaunchAsync 在未打开浏览器的情况下挂起

为什么这个同步运行的异步睡眠会挂起?

正则表达式在空格上拆分,除非在引号中

将字符串与容差进行比较

判断 Windows 路径中是否存在可执行文件

将毫秒转换为人类可读的时间间隔

maxRequestLength 的最大值?

重新启动(回收)应用程序池

C#:内存不足异常

何时在 C#/.NET 中使用指针?

C#中的引用类型

String.Replace() 与 StringBuilder.Replace()

如何重新启动 WPF 应用程序?

如何为我的 C# 应用程序创建产品密钥?

实体框架太慢了.我有哪些 Select ?

在 .NET 中乘以时间跨度

有没有一种简单的方法来判断 .NET Framework 版本?