对结构的反思与类不同 - 但仅在代码中
代码片段:
Dim target As Object
' target gets properly set to something of the desired type
Dim field As FieldInfo = target.GetType.GetField("fieldName", _
BindingFlags.Instance Or BindingFlags.Public Or BindingFlags.NonPublic)
field.SetValue(target,newValue)
如果目标设置为 CLASS 的实例,则此片段可以完美运行。
但是,如果目标设置为 STRUCTURE 的实例,则代码实际上不会更改字段的值。 没有错误,但值保持不变。
而且,奇怪的是,如果我单步执行代码,会发现 SetValue 无法执行任何操作,然后立即转到“立即”窗口并键入完全相同的 SetValue 操作,有效。
关于正在发生的事情以及如何实际更改代码中的字段有什么建议吗?
编辑:
根据 Jon Skeet 的请求,实际代码:
Private Shared Function XmlDeserializeObject(ByVal objectType As Type, _
ByVal deserializedID As String) As Object
Dim result As Object
result = CreateObject(objectType)
mXmlR.ReadStartElement()
Do While mXmlR.IsStartElement _
AndAlso mXmlR.Name <> elementItem
Dim field As FieldInfo = result.GetType.GetField(FullName, _
BindingFlags.Instance Or BindingFlags.Public Or BindingFlags.NonPublic)
field.SetValue(result, XmlDeserialize(field.FieldType))
Loop
Return result
End Function
外部变量和调用的例程:
* mXmlR 是一个 XmlTextReader,并且已正确初始化和定位(否则这不适用于类)
* CreateObject 工作(同上)
* XmlDeserialize 大部分工作,并且在问题点上处理整数就很好了。 唯一已知的问题是结构问题。
至于我如何检查该值,我主要查看“本地”窗口,但我也在“立即”窗口中使用了打印语句,并且我正在运行一个 NUnit 测试,该测试由于这个问题而失败 - 而通过类(而不是结构)的等效测试。
这是测试。
<Serializable()> Private Structure SimpleStructure
Public MemberOne As Integer
End Structure
<Test()> Sub A016_SimpleStructure()
Dim input As New SimpleStructure
input.MemberOne = 3
Dim st As String = Serialize(input)
Debug.Print(st)
Dim retObject As Object = Deserialize(st)
Assert.IsNotNull(retObject)
Assert.IsInstanceOfType(GetType(SimpleStructure), retObject)
Assert.AreEqual(input.MemberOne, DirectCast(retObject, SimpleStructure).MemberOne)
End Sub
Code snippet:
Dim target As Object
' target gets properly set to something of the desired type
Dim field As FieldInfo = target.GetType.GetField("fieldName", _
BindingFlags.Instance Or BindingFlags.Public Or BindingFlags.NonPublic)
field.SetValue(target,newValue)
This snippet works perfectly IF target is set to an instance of a CLASS.
However, if target is set to an instance of a STRUCTURE, the code does not actually change the value of the field. No error, but the value remains unchanged.
And, oddly, if I'm stepping through code, watch the SetValue fail to do anything, and immediately go to the Immediate window and type exactly the same SetValue operation, that works.
Any suggestions on what's going on and how to actually change the field IN CODE?
Edit:
Per request from Jon Skeet, actual code:
Private Shared Function XmlDeserializeObject(ByVal objectType As Type, _
ByVal deserializedID As String) As Object
Dim result As Object
result = CreateObject(objectType)
mXmlR.ReadStartElement()
Do While mXmlR.IsStartElement _
AndAlso mXmlR.Name <> elementItem
Dim field As FieldInfo = result.GetType.GetField(FullName, _
BindingFlags.Instance Or BindingFlags.Public Or BindingFlags.NonPublic)
field.SetValue(result, XmlDeserialize(field.FieldType))
Loop
Return result
End Function
External variables and called routines:
* mXmlR is an XmlTextReader, and is properly initialized and positioned (else this would not work on classes)
* CreateObject works (ditto)
* XmlDeserialize mostly works, and at the point in question is handling an integer just fine. The only known problem is with structures.
As for how I'm checking the value, I'm mostly looking at the Locals window, but I've also used print statements in the Immediate window, and I'm running an NUnit test which is failing because of this problem - while the equivalent test with a class, rather than a structure, passes.
Here's the test.
<Serializable()> Private Structure SimpleStructure
Public MemberOne As Integer
End Structure
<Test()> Sub A016_SimpleStructure()
Dim input As New SimpleStructure
input.MemberOne = 3
Dim st As String = Serialize(input)
Debug.Print(st)
Dim retObject As Object = Deserialize(st)
Assert.IsNotNull(retObject)
Assert.IsInstanceOfType(GetType(SimpleStructure), retObject)
Assert.AreEqual(input.MemberOne, DirectCast(retObject, SimpleStructure).MemberOne)
End Sub
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
使用您的原始示例,我同意它可以在 C# 中运行,但不能在 VB 中运行! 如果您使用 Reflector 或 ILDasm,您将看到对 Field.SetValue(target, ...) 的调用实际上被编译(在 VB 中)为:
GetObjectValue “如果 obj 是值类,则返回装箱副本;否则返回 obj 本身已返回。” 即该值正在您的结构的副本上设置!
此链接 给出解释(例如)。 解决方法是将目标声明为 System.ValueType 而不是 Object。 我不确定这是否真的对您的现实代码有帮助:您可能需要一个混乱的类型测试才能将值类型与引用类型分开处理。
Working with your original sample, I agree that it works in C# but not in VB! If you use Reflector or ILDasm you will see that the call to Field.SetValue(target, ...) is actually compiled (in VB) as:
GetObjectValue "Returns a boxed copy of obj if it is a value class; otherwise obj itself is returned." I.e. the value is being set on a copy of your struct!
This link gives the explanation (such as it is). The workaround is to declare target as System.ValueType instead of Object. I'm not sure if that actually helps in your real-life code: you may need a messy type test to be able to handle value types separately from reference types.
问题在于 VB 制作了对象的副本,并且 setvalue 指令适用于该副本,但不适用于对象本身。 解决方法是通过辅助 var 和 CType 函数恢复对原始对象的更改。 在以下示例中,我们希望将 champion 变量的 country 字段设置为 Spain(champion 是*St_WorldChampion* 结构)。 我们在 x 变量中进行更改,然后将它们复制到 champion 变量。 有用。
The problem is that VB makes a copy of the object and the setvalue instruction applies to the copy, but not to the object itself. The workaround is to restore the changes to the original object through an auxliar var and the CType function. In the following example, we want to set the country field of the champion var to Spain (champion is a *St_WorldChampion* structure). We make the changes in the x var, an then we copy them to the champion var. It works.
好吧,您还没有显示所有代码 - 特别是您在哪里设置
target
以及之后如何检查字段的值。下面是一个示例,显示它在 C# 中运行良好:(
我很确定语言的选择与这里无关,但通过更多代码我们可以确定。)我强烈怀疑你正在做类似的事情:
上面代码片段中
foo
的值不会改变 - 因为在第二行,该值在装箱时被复制。 之后您需要拆箱并再次复制:如果不是这样,请显示一个简短但完整的程序来演示该问题。
Well, you haven't shown all your code - in particular, where you're setting
target
and how you're checking the value of the field afterwards.Here's an example which shows it working fine in C# though:
(I'm pretty sure the choice of language isn't relevant here, but with more code we could tell for certain.) My strong suspicion is that you're doing something like:
The value of
foo
in the snippet above won't have changed - because on the second line, the value is copied when it's boxed. You'd need to unbox and copy again afterwards:If that's not it, please show a short but complete program which demonstrates the problem.
您好,我使用基督教示例创建了此功能,希望它有所帮助。
该函数使用的属性也受到影响
Hi I made this function using christian example, hope it helps.
This Function uses Properties which also are affected