0

编辑:根据 LoveMeSomeCode 的回答,我相信这个问题只出现在 VB.Net 中。

我试图通过将更改的属性的旧值保存在字典中并在需要恢复时通过反射设置它们来将类恢复到以前的状态。我遇到了一个问题,如果旧值是 Nothing (null),我在尝试设置属性时会得到一个空引用异常。这是我尝试过的。

假设每个循环都像这样:

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

方法一:

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

方法二:

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

方法三:

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

对于这些方法中的每一个,当 pair.Value 为空时,我都会得到一个空引用异常。setter 能够保存一个空值(通常该属性是一个字符串)。我做错了什么或者我该如何解决?

编辑:如果我也直接将它传递给 null,则每种方法都会失败。

编辑:如果他们帮助任何人,这里是堆栈跟踪:

方法 1 System.NullReferenceException:对象引用未设置为对象的实例。在 Microsoft.VisualBasic.CompilerServices.Symbols.Container.InvokeMethod(Method TargetProcedure, Object[] Arguments, Boolean[] CopyBack, BindingFlags Flags) 在 Microsoft.VisualBasic.CompilerServices.NewLateBinding.LateSet(Object Instance, Type Type, String MemberName, Object [] Arguments, String[] ArgumentNames, Type[] TypeArguments, Boolean OptimisticSet, Boolean RValueBase, CallType CallType) at Microsoft.VisualBasic.CompilerServices.Versioned.CallByName(Object Instance, String MethodName, CallType UseCallType, Object[] Arguments) 在myProject .Presenter.CustomerDetailPresenter.RevertCustomer() 在我的文件中:第 378 行

方法 2 System.Reflection.TargetInvocationException:调用的目标已抛出异常。---> System.NullReferenceException:对象引用未设置为对象的实例。在我的项目.Presenter.CustomerDetailPresenter.set_City(String value) --- 内部异常堆栈跟踪结束 --- 在 System.RuntimeMethodHandle._InvokeMethodFast(Object target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner) 在 System.RuntimeMethodHandle .InvokeMethodFast(Object target, Object[] arguments, Signature sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner) at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfoculture, Boolean skipVisibilityChecks) at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfoculture) at System.RuntimeType.InvokeMember(String name, BindingFlags bindingFlags, Binder binder,System.Type.InvokeMember(String name, BindingFlags invokeAttr, Binder binder, Object target, Object[] args) at System.Type.InvokeMember(String name, BindingFlags invokeAttr, Binder binder, Object target, Object[] args) 在myProject .Presenter.CustomerDetailPresenter.RevertCustomer ()

方法 3 System.Reflection.TargetInvocationException:调用的目标已抛出异常。---> System.NullReferenceException:对象引用未设置为对象的实例。在我的项目.Presenter.CustomerDetailPresenter.set_City(String value) --- 内部异常堆栈跟踪结束 --- 在 System.RuntimeMethodHandle._InvokeMethodFast(Object target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner) 在 System.RuntimeMethodHandle .InvokeMethodFast(对象目标,对象 [] 参数,签名 sig,MethodAttributes 方法属性,RuntimeTypeHandle typeOwner)在 System.Reflection.RuntimeMethodInfo.Invoke(对象 obj,BindingFlags invokeAttr,Binder binder,Object[] 参数,CultureInfo 文化,布尔 skipVisibilityChecks)

在 System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfoculture) at System.RuntimeType.InvokeMember(String name, BindingFlags bindingFlags, Binder binder, Object target, Object[] providedArgs, ParameterModifier[] 修饰符,CultureInfo 文化,String[] namedParams) 在 System.Type.InvokeMember(String name, BindingFlags invokeAttr, Binder binder, Object target, Object[] args) 在myProject .Presenter.CustomerDetailPresenter.RevertCustomer ()

4

3 回答 3

3

您在第二个和第三个选项堆栈跟踪中看到这一点的事实

System.NullReferenceException:对象引用未设置为对象的实例。在 myProject.Presenter.CustomerDetailPresenter.set_City(字符串值)

让我觉得您的 CustomerDetailPresenter.City 属性设置器中有一些东西没有处理空值。您的属性设置器的实现是什么?那是任何可能失败的验证或审计代码吗?

2009 年 3 月 24 日更新: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(字符串值)

set_City() setter 是引发异常的事实,表明该方法已被成功找到并调用。正在按请求传入 null(nothing) 值。因此,错误不在反射中,而是在调用属性设置器时发生的事情。您可能已经尝试过,但是在设置器中设置断点或设置 IDE 以中断所有托管异常以查看是否可以捕获实际原因?或者,存储的属性信息的状态是预期的吗?名称、类型和值是否同步?

希望这可以帮助。

于 2009-03-17T19:04:01.937 回答
0

好吧,这是 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(),它们将被恢复。它似乎至少对字符串有效。放在基类中似乎是一组很好的东西。

于 2009-01-23T15:22:15.730 回答
0

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)
于 2009-01-23T00:38:58.477 回答