Reflection on structure differs from class - but only in code

前端 未结 4 1853
执笔经年
执笔经年 2020-12-21 01:11

Code snippet:

Dim target As Object
\' target gets properly set to something of the desired type
Dim field As FieldInfo = target.GetType.GetField(\"fieldName\         


        
相关标签:
4条回答
  • 2020-12-21 01:53

    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.

    0 讨论(0)
  • 2020-12-21 01:58

    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.

    0 讨论(0)
  • 2020-12-21 01:58

    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
    
    0 讨论(0)
  • 2020-12-21 02:00

    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
    
    0 讨论(0)
提交回复
热议问题