使用反射将属性值设置为Nothing(Null)

时间:2009-01-23 00:00:47

标签: .net vb.net reflection null

编辑:基于LoveMeSomeCode的答案,我相信这个问题只出现在VB.Net中。

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

假设a为每个循环:

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时,我得到一个空引用异常。 setter能够保存null值(通常属性是字符串)。我做错了什么或者我该如何解决它?

编辑:如果我直接传递null,每个方法都会失败。

编辑:以下是堆栈跟踪,如果它们可以帮助任何人:

方法1 System.NullReferenceException:未将对象引用设置为对象的实例。    在Microsoft.VisualBasic.CompilerServices.Symbols.Container.InvokeMethod(Method TargetProcedure,Object [] Arguments,Boolean [] CopyBack,BindingFlags Flags)    at 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)    at myProject myfile 中的Preseter.CustomerDetailPresenter.RevertCustomer():第378行

方法2 System.Reflection.TargetInvocationException:调用目标抛出了异常。 ---> System.NullReferenceException:未将对象引用设置为对象的实例。    at myProject .Presenter.CustomerDetailPresenter.set_City(String value)    ---内部异常堆栈跟踪结束---    at System.RuntimeMethodHandle._InvokeMethodFast(Object target,Object [] arguments,SignatureStruct& sig,MethodAttributes methodAttributes,RuntimeTypeHandle typeOwner)    at 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,CultureInfo culture,Boolean skipVisibilityChecks)    在System.Reflection.RuntimeMethodInfo.Invoke(Object obj,BindingFlags invokeAttr,Binder binder,Object []参数,CultureInfo文化)    at System.RuntimeType.InvokeMember(String name,BindingFlags bindingFlags,Binder binder,Object target,Object [] providedArgs,ParameterModifier [] modifiers,CultureInfo culture,String [] namedParams)    at System.Type.InvokeMember(String name,BindingFlags invokeAttr,Binder binder,Object target,Object [] args)    在 myProject .Presenter.CustomerDetailPresenter.RevertCustomer()

方法3 System.Reflection.TargetInvocationException:调用目标抛出了异常。 ---> System.NullReferenceException:未将对象引用设置为对象的实例。    at myProject .Presenter.CustomerDetailPresenter.set_City(String value)    ---内部异常堆栈跟踪结束---    at System.RuntimeMethodHandle._InvokeMethodFast(Object target,Object [] arguments,SignatureStruct& sig,MethodAttributes methodAttributes,RuntimeTypeHandle typeOwner)    at 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,CultureInfo culture,Boolean skipVisibilityChecks)

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

3 个答案:

答案 0 :(得分:3)

您在第2和第3选项堆栈跟踪中看到此事实

  

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

让我觉得你的CustomerDetailPresenter.City属性setter中有一些东西没有处理null值。您的属性设置器的实现是什么?是否有任何失败的验证或审核代码?

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:Object   引用未设置为的实例   宾语。在   myProject.Presenter.CustomerDetailPresenter.set_City(字符串   值)

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

希望这有帮助。

答案 1 :(得分:0)

SetValueConvert.ChangeType调用IConvertible上的pair.Value方法时,当您尝试在null个实例上调用它们时,它们肯定会失败。

检查pair.Value是否为null,如果是这种情况,则传递明确的null

InvokeMember期望数组为第5个参数。尝试:

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

答案 2 :(得分: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()然后它们将被恢复。它似乎至少对字符串起作用。它似乎是一组很好的东西放在基类中。