对结构的反思与类不同 - 但仅在代码中

发布于 2024-07-12 13:15:33 字数 1972 浏览 5 评论 0原文

代码片段:

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

时光清浅 2024-07-19 13:15:33

使用您的原始示例,我同意它可以在 C# 中运行,但不能在 VB 中运行! 如果您使用 Reflector 或 ILDasm,您将看到对 Field.SetValue(target, ...) 的调用实际上被编译(在 VB 中)为:

field.SetValue(RuntimeHelpers.GetObjectValue(target), ...)

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:

field.SetValue(RuntimeHelpers.GetObjectValue(target), ...)

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.

孤芳又自赏 2024-07-19 13:15:33

问题在于 VB 制作了对象的副本,并且 setvalue 指令适用于该副本,但不适用于对象本身。 解决方法是通过辅助 var 和 CType 函数恢复对原始对象的更改。 在以下示例中,我们希望将 champion 变量的 country 字段设置为 Spainchampion 是*St_WorldChampion* 结构)。 我们在 x 变量中进行更改,然后将它们复制到 champion 变量。 有用。

Public Structure St_WorldChampion
    Dim sport As String
    Dim country As String
End Structure

Sub UpdateWorldChampion()
    Dim champion As New St_WorldChampion, x As ValueType
    Dim prop As System.Reflection.FieldInfo

    ' Initial values: Germany was the winner in 2006
    champion.country = "Germany"
    champion.sport = "Football"

    ' Update the World Champion: Spain since 2010
    x = champion
    prop = x.GetType().GetField("country")
    prop.SetValue(x, "Spain")
    champion = CType(x, St_WorldChampion)

End Sub

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.

Public Structure St_WorldChampion
    Dim sport As String
    Dim country As String
End Structure

Sub UpdateWorldChampion()
    Dim champion As New St_WorldChampion, x As ValueType
    Dim prop As System.Reflection.FieldInfo

    ' Initial values: Germany was the winner in 2006
    champion.country = "Germany"
    champion.sport = "Football"

    ' Update the World Champion: Spain since 2010
    x = champion
    prop = x.GetType().GetField("country")
    prop.SetValue(x, "Spain")
    champion = CType(x, St_WorldChampion)

End Sub
你的背包 2024-07-19 13:15:33

好吧,您还没有显示所有代码 - 特别是您在哪里设置 target 以及之后如何检查字段的值。

下面是一个示例,显示它在 C# 中运行良好:(

using System;
using System.Reflection;

struct Foo
{
    public int x;
}

class Test
{
    static void Main()
    {
        FieldInfo field = typeof(Foo).GetField("x");

        object foo = new Foo();
        field.SetValue(foo, 10);
        Console.WriteLine(((Foo) foo).x);
    }
}

我很确定语言的选择与这里无关,但通过更多代码我们可以确定。)我强烈怀疑你正在做类似的事情:

Foo foo = new Foo();
object target = foo;
// SetValue stuff

// What do you expect foo.x to be here?

上面代码片段中 foo 的值不会改变 - 因为在第二行,该值在装箱时被复制。 之后您需要拆箱并再次复制:

foo = (Foo) target;

如果不是这样,请显示一个简短但完整的程序来演示该问题。

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:

using System;
using System.Reflection;

struct Foo
{
    public int x;
}

class Test
{
    static void Main()
    {
        FieldInfo field = typeof(Foo).GetField("x");

        object foo = new Foo();
        field.SetValue(foo, 10);
        Console.WriteLine(((Foo) foo).x);
    }
}

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

Foo foo = new Foo();
object target = foo;
// SetValue stuff

// What do you expect foo.x to be here?

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:

foo = (Foo) target;

If that's not it, please show a short but complete program which demonstrates the problem.

り繁华旳梦境 2024-07-19 13:15:33

您好,我使用基督教示例创建了此功能,希望它有所帮助。
该函数使用的属性也受到影响

''' <summary>
''' Establece el -valor- en la -propiedad- en el -objeto-
''' Sets Value in Objeto.[propertyname]
''' </summary>
''' <param name="objeto">Object where we will set this property</param>
''' <param name="Propiedad">Name of the property</param>
''' <param name="valor">New Value of the property</param>
''' <returns>Object with changed property</returns>
''' <remarks>It works on structures!</remarks>
Function Establecer_propiedad(objeto As Object, Propiedad As String, valor As Object) As Object
    'Check arguments
    If objeto Is Nothing Then Throw New ArgumentNullException("Objeto")
    If String.IsNullOrWhiteSpace(Propiedad) Then Throw New ArgumentNullException("Propiedad")
    'Get the object type
    Dim t As Type = objeto.GetType
    'Get the propertyInfo by its name
    Dim prop As PropertyInfo = t.GetProperty(Propiedad)
    'Check if the property exist
    If prop Is Nothing Then Throw New InvalidOperationException("Property does not exist")
    If Not prop.CanWrite Then Throw New InvalidOperationException("Property is read only")
    'Determine if it is a class or a structure
    If Not t.IsValueType Then ' (it is a reference value)
        'Set without troubles
        If prop.CanWrite Then prop.SetValue(objeto, valor)
        'Return object
        Return objeto
    Else '(It is a structure)
        'Create a box using a valuetype
        'It doesnot work in object
        Dim Box As ValueType
        'Set item in box
        Box = objeto
        'Set value in box
        prop.SetValue(Box, valor)
        'Return box
        Return Box
    End If
End Function

Hi I made this function using christian example, hope it helps.
This Function uses Properties which also are affected

''' <summary>
''' Establece el -valor- en la -propiedad- en el -objeto-
''' Sets Value in Objeto.[propertyname]
''' </summary>
''' <param name="objeto">Object where we will set this property</param>
''' <param name="Propiedad">Name of the property</param>
''' <param name="valor">New Value of the property</param>
''' <returns>Object with changed property</returns>
''' <remarks>It works on structures!</remarks>
Function Establecer_propiedad(objeto As Object, Propiedad As String, valor As Object) As Object
    'Check arguments
    If objeto Is Nothing Then Throw New ArgumentNullException("Objeto")
    If String.IsNullOrWhiteSpace(Propiedad) Then Throw New ArgumentNullException("Propiedad")
    'Get the object type
    Dim t As Type = objeto.GetType
    'Get the propertyInfo by its name
    Dim prop As PropertyInfo = t.GetProperty(Propiedad)
    'Check if the property exist
    If prop Is Nothing Then Throw New InvalidOperationException("Property does not exist")
    If Not prop.CanWrite Then Throw New InvalidOperationException("Property is read only")
    'Determine if it is a class or a structure
    If Not t.IsValueType Then ' (it is a reference value)
        'Set without troubles
        If prop.CanWrite Then prop.SetValue(objeto, valor)
        'Return object
        Return objeto
    Else '(It is a structure)
        'Create a box using a valuetype
        'It doesnot work in object
        Dim Box As ValueType
        'Set item in box
        Box = objeto
        'Set value in box
        prop.SetValue(Box, valor)
        'Return box
        Return Box
    End If
End Function
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文