Фрагмент кода:
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





Что ж, вы не показали весь свой код - в частности, где вы устанавливаете 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;
Если это не так, покажите короткую, но полный программу, которая демонстрирует проблему.
Работая с вашим исходным образцом, я согласен, что он работает на 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
Здесь уместен язык: VB.net потребуется «ValueType foo = new Foo ();» эквивалент (вместо вашей строки «object foo = new Foo ();»: по какой-то причине, которая в настоящее время мне не ясна, использование переменной типа объекта в VB.net имеет другое поведение).