对结构的反思不同于类 - 但仅限于代码

时间:2009-01-15 19:36:23

标签: vb.net .net-2.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的实例,则此代码段完美无缺。

但是,如果将target设置为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主要起作用,并且在问题上处理一个整数就好了。唯一已知的问题是结构。

至于我如何检查值,我主要看的是Locals窗口,但我也在Immediate窗口中使用了print语句,而且我正在运行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

4 个答案:

答案 0 :(得分:5)

使用原始样本,我同意它适用于C#但不适用于VB!如果使用Reflector或ILDasm,您将看到对Field.SetValue(target,...)的调用实际上是在(在VB中)编译为:

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

GetObjectValue“返回obj的盒装副本(如果它是值类;否则返回obj本身。”即该值正在您的结构的副本上设置!

This link给出了解释(例如它)。解决方法是将target声明为System.ValueType而不是Object。我不确定这是否真的有助于您的现实代码:您可能需要一个凌乱的类型测试才能够与引用类型分开处理值类型。

答案 1 :(得分:3)

问题是VB制作了对象的副本,而setvalue指令适用于副本,但不适用于对象本身。解决方法是通过auxliar var和CType函数恢复对原始对象的更改。在以下示例中,我们要将冠军 var的 country 字段设置为 Spain champion 是一个* St_WorldChampion *结构)。我们在 x var中进行更改,然后将它们复制到 champion var。它有效。

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

答案 2 :(得分:1)

好吧,您没有显示所有代码 - 尤其是您正在设置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;

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

答案 3 :(得分:0)

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

''' <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