Отражение структуры отличается от класса - но только в коде

Фрагмент кода:

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)

Этот фрагмент отлично работает, ЕСЛИ target установлен на экземпляр CLASS.

Однако, если target установлен на экземпляр STRUCTURE, код фактически не изменяет значение поля. Ошибки нет, но значение остается неизменным.

И, как ни странно, если я пошагово прохожу код, наблюдаю, как SetValue ничего не делает, и сразу же перехожу в окно Immediate и набираю точно такую ​​же операцию SetValue, это работает.

Есть предложения о том, что происходит и как на самом деле изменить поле В КОДЕ?

Редактировать:

По запросу Джона Скита фактический код:

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, и я запускаю тест 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
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
5
0
3 779
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Что ж, вы не показали весь свой код - в частности, где вы устанавливаете 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;

Если это не так, покажите короткую, но полный программу, которая демонстрирует проблему.

Здесь уместен язык: VB.net потребуется «ValueType foo = new Foo ();» эквивалент (вместо вашей строки «object foo = new Foo ();»: по какой-то причине, которая в настоящее время мне не ясна, использование переменной типа объекта в VB.net имеет другое поведение).

Giuseppe 13.10.2014 18:03
Ответ принят как подходящий

Работая с вашим исходным образцом, я согласен, что он работает на C#, но не на VB! Если вы используете Reflector или ILDasm, вы увидите, что вызов Field.SetValue (target, ...) фактически компилируется (в VB) как:

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

GetObjectValue "Возвращает упакованную копию obj, если это класс значений; в противном случае возвращается сам объект obj." Т.е. значение устанавливается на копию вашей структуры!

Эта ссылка дает объяснение (такое, какое оно есть). Обходной путь - объявить цель как System.ValueType вместо Object. Я не уверен, действительно ли это помогает в вашем реальном коде: вам может потребоваться беспорядочный тест типов, чтобы иметь возможность обрабатывать типы значений отдельно от ссылочных типов.

Проблема в том, что VB делает копию объекта, и инструкция setvalue применяется к копии, но не к самому объекту. Обходной путь - восстановить изменения исходного объекта с помощью вспомогательной переменной и функции CType. В следующем примере мы хотим установить для поля страна переменной чемпион значение Испания (чемпион - это структура * St_WorldChampion *). Мы вносим изменения в переменную Икс, а затем копируем их в переменную чемпион. Оно работает.

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

Привет, я сделал эту функцию на христианском примере, надеюсь, это поможет. Эта функция использует свойства, на которые также влияют

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

Другие вопросы по теме