Используйте отражение, чтобы установить для свойства значение Nothing (Null) - PullRequest
0 голосов
/ 23 января 2009

Редактировать: Основываясь на ответе LoveMeSomeCode, я считаю, что эта проблема появляется только в VB.Net.

Я пытаюсь вернуть класс в предыдущее состояние, сохраняя старые значения измененных свойств в словаре и устанавливая их с помощью отражения, когда мне нужно вернуться. У меня возникла проблема, когда, если старое значение Nothing (null), я получаю исключение нулевой ссылки при попытке установить свойство. Вот что я попробовал.

Предположим, для каждого цикла, как это:

For Each pair As KeyValuePair(Of String, Object) In myOldValues
...
Next

Метод 1:

CallByName(Me, pair.Key, CallType.Set, pair.Value)

Метод 2:

Me.GetType().InvokeMember(pair.Key, Reflection.BindingFlags.SetProperty, Nothing, Me, pair.Value)

Метод 3:

Dim propInfo As System.Reflection.PropertyInfo = Me.GetType.GetProperty(pair.Key)
propInfo.SetValue(Me, Convert.ChangeType(pair.Value, propInfo.PropertyType), Nothing)

Для каждого из этих методов я получаю исключение нулевой ссылки, когда pair.Value равно null. Сеттер может содержать нулевое значение (часто это свойство является строкой). Что я делаю не так или как мне обойти это?

Редактировать: Каждый метод завершается ошибкой, если я также передаю его как ноль напрямую.

Редактировать: Вот трассировки стека, если они кому-нибудь помогут:

Метод 1 System.NullReferenceException: ссылка на объект не установлена ​​на экземпляр объекта. в Microsoft.VisualBasic.CompilerServices.Symbols.Container.InvokeMethod (метод TargetProcedure, аргументы Object [], логический [] CopyBack, флаги BindingFlags) в Microsoft.VisualBasic.CompilerServices.NewLateBinding.LateSet (Экземпляр объекта, Тип Тип, Строка MemberName, Объект [] Аргументы, Строка [] ArgumentNames, Тип [] Тип Аргументы, Логический OptimisticSet, Логический RValueBase, CallType CallType) в Microsoft.VisualBasic.CompilerServices.Versioned.CallByName (Экземпляр объекта, String MethodName, CallType UseCallType, Object [] Аргументы) в myProject .Presenter.CustomerDetailPresenter.RevertCustomer () в myfile : строка 378

Метод 2 System.Reflection.TargetInvocationException: исключение было сгенерировано целью вызова. ---> System.NullReferenceException: ссылка на объект не установлена ​​для экземпляра объекта. в myProject .Presenter.CustomerDetailPresenter.set_City (строковое значение) --- Конец внутренней трассировки стека исключений --- в System.RuntimeMethodHandle._InvokeMethodFast (Объектная цель, аргументы Object [], SignatureStruct & sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner) at System.RuntimeMethodHandle.InvokeMethodFast (Объектная цель, аргументы Object [], сигнатура сигнатуры, метод MethodAttributesAttributes, RuntimeTypeHandle typeOwner) в System.Reflection.RuntimeMethodInfo.Invoke (Object obj, BindingFlags invokeAttr, Binder Binder, параметры Object [], CultureInfo culture, логическое skipVisibilityChecks) в System.Reflection.RuntimeMethodInfo.Invoke (Object obj, BindingFlags invokeAttr, Binder binder, Object [] параметры, CultureInfo culture) в System.RuntimeType.InvokeMember (имя строки, BindingFlags bindingFlags, связыватель связывания, объектная цель, Object [] provideArgs, модификаторы ParameterModifier [], CultureInfo culture, String [] namedParams) в System.Type.InvokeMember (имя строки, BindingFlags invokeAttr, связыватель Binder, цель объекта, аргументы объекта []) в myProject .Presenter.CustomerDetailPresenter.RevertCustomer ()

Метод 3 System.Reflection.TargetInvocationException: исключение было сгенерировано целью вызова. ---> System.NullReferenceException: ссылка на объект не установлена ​​для экземпляра объекта. в myProject .Presenter.CustomerDetailPresenter.set_City (строковое значение) --- Конец внутренней трассировки стека исключений --- в System.RuntimeMethodHandle._InvokeMethodFast (Объектная цель, аргументы Object [], SignatureStruct & sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner) at System.RuntimeMethodHandle.InvokeMethodFast (Объектная цель, аргументы Object [], сигнатура сигнатуры, метод MethodAttributesAttributes, RuntimeTypeHandle typeOwner)в System.Reflection.RuntimeMethodInfo.Invoke (Object obj, BindingFlags invokeAttr, Binder Binder, параметры Object [], CultureInfo culture, логическое skipVisibilityChecks)

в System.Reflection.RuntimeMethodInfo.Invoke (Object obj, BindingFlags invokeAttr, связыватель Binder, параметры Object [], культура CultureInfo) в System.RuntimeType.InvokeMember (имя строки, BindingFlags bindingFlags, связыватель связывания, объектная цель, Object [] provideArgs, модификаторы ParameterModifier [], CultureInfo culture, String [] namedParams) в System.Type.InvokeMember (имя строки, BindingFlags invokeAttr, связыватель Binder, цель объекта, аргументы объекта []) в myProject .Presenter.CustomerDetailPresenter.RevertCustomer ()

Ответы [ 3 ]

3 голосов
/ 17 марта 2009

Тот факт, что вы видите это во 2-й и 3-й трассировке стека опций

System.NullReferenceException: объект ссылка не установлена ​​на экземпляр объект. в myProject.Presenter.CustomerDetailPresenter.set_City (String значение)

заставляет меня думать, что в вашем параметре свойства CustomerDetailPresenter.City есть что-то, что не обрабатывает нулевое значение. Какова реализация вашего свойства setter? Это какой-либо код проверки или проверки, который может давать сбой?

Обновление 03-24-2009: Быстрый тест на VB, и этот код работает как задумано. Я попытался описать сценарий, который вы описали.

Мой тестовый класс, чье свойство установлено (частично):

Public Class MyObject

    Private mId As Integer
    Private mName As String
    Private mDOB As Date
     .......
     .......
    Public Property Name() As String
        Get
            Return mName
        End Get
        Set(ByVal Value As String)
            mName = Value
        End Set
    End Property

Я создал класс PropertyState, который будет содержать имя свойства, значение и тип. И код для динамического задания свойства:

Private Sub SetValues()
        'get object that we are working with
        Dim ty As Type = mObjectInstance.GetType

        'create our property name/value info
        Dim info As New PropertyState
        With info
            .PropName = "Name"
            .OriginalValue = Nothing
            .ValueType = GetType(String)
        End With

        'now use reflection to set value on object
        Dim prop As PropertyInfo = ty.GetProperty("Name", BindingFlags.Instance Or BindingFlags.Public)
        'use Convert.ChangeType to duplicate problem scenario
        Dim newValue = Convert.ChangeType(Nothing, GetType(String))
        'prop.SetValue(mObjectInstance, newValue, BindingFlags.Instance Or BindingFlags.Public, Nothing, Nothing, Globalization.CultureInfo.CurrentUICulture)
        prop.SetValue(mObjectInstance, Convert.ChangeType(info.OriginalValue, info.ValueType), Nothing)

        DisplayValues(CType(mObjectInstance, MyObject))
    End Sub

Я использовал две разные перегрузки метода SetValue, я обнаружил, что неэкспонентная установка BindingFlags может иногда вызывать проблемы с отражением. Однако в этом случае оба перекрытия работают нормально.

Итак, я оглядываюсь назад на трассировку стека, которую вы разместили в своем вопросе:

System.NullReferenceException: объект ссылка не установлена ​​на экземпляр объект. в myProject.Presenter.CustomerDetailPresenter.set_City (String значение)

тот факт, что метод set_City () вызывает исключение, указывает на то, что метод найден и успешно вызван. Значение NULL (ничего) передается в соответствии с запросом. Итак, ошибка не в отражении, а в том, что происходит в результате вызова метода установки свойства. Возможно, вы уже пробовали это сделать, но устанавливаете точку останова в установщике или настраиваете среду IDE для прерывания всех управляемых исключений, чтобы увидеть, можете ли вы зафиксировать действительную причину? Или состояние сохраненной информации о свойствах соответствует ожидаемому? Имя, тип и значение все в синхронизации?

Надеюсь, это поможет.

0 голосов
/ 23 января 2009

Ну, это C # вместо VB.NET, но, похоже, это работает:

private Dictionary<string, object> MemoryValues = new Dictionary<string, object>();

        public void Store()
        {
            foreach (PropertyInfo info in this.GetType().GetProperties())
            {
                if (MemoryValues.ContainsKey(info.Name))
                    MemoryValues[info.Name] = info.GetValue(this, null);
                else
                    MemoryValues.Add(info.Name, info.GetValue(this, null));
            }
        }

        public void Recall()
        {
            foreach (PropertyInfo info in this.GetType().GetProperties())
            {
                info.SetValue(this, MemoryValues[info.Name], null);
            }
        }

, чтобы вы могли установить свойства и вызвать Store (), и они будут сохранены в словаре. Затем вы можете изменить их и вызвать Recall (), и они будут восстановлены. Кажется, что для строк будет работать ноль, по крайней мере. Кажется, это хороший набор вещей для базового класса.

0 голосов
/ 23 января 2009

В SetValue, Convert.ChangeType вызывает IConvertible методы для pair.Value, они, безусловно, завершаются ошибкой при попытке вызвать их в null экземпляре.

Проверьте pair.Value на пустоту и передайте явное null, если это так.

InvokeMember ожидает массив в качестве 5-го аргумента. Попробуйте:

Params(0) = pair.Value
Me.GetType().InvokeMember(pair.Key, Reflection.BindingFlags.SetProperty, Nothing, Me, Params)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...