2009-01-23 73 views
0

编辑:基于LoveMeSomeCode的答案,我相信这个问题只出现在VB.Net中。使用反射设置属性值为Nothing(空)

我想通过在字典中保存已更改属性的旧值并在需要恢复时通过反射设置它们来将类恢复为以前的状态。我有一个问题,如果旧值是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时,我会得到一个空引用异常。 setter能够保存一个空值(通常该属性是一个字符串)。我做错了什么,或者我该如何解决它?

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

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

方法1 System.NullReferenceException:对象不设置到对象的实例。 at 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 []参数,String [] ArgumentNames,Type [] TypeArguments,布尔OptimisticSet,布尔RValueBase,CallType CallType)在Microsoft.VisualBasic.CompilerServices.Versioned.CallByName(对象实例,字符串MethodName,CallType UseCallType,Object []参数 参数) 在MYFILEmyProject的 .Presenter.CustomerDetailPresenter.RevertCustomer():线378

方法2 System.Reflection.TargetInvocationException:调用的目标引发了异常。 ---> System.NullReferenceException:未将对象引用设置为对象的实例。 在myProject的 .Presenter.CustomerDetailPresenter.set_City(字符串值) ---内部异常堆栈跟踪的结尾--- 在System.RuntimeMethodHandle._InvokeMethodFast(对象目标,对象[]参数,SignatureStruct & SIG,MethodAttributes methodAttributes ,运行时类型句柄typeOwner) at System.RuntimeMethodHandle.InvokeMethodFast(Object object,Object [] arguments,Signature sig,MethodAttributes methodAttributes,RuntimeTypeHandle typeOwner) at System.Reflection.RuntimeMethodInfo.Invoke(Object obj,BindingFlags invokeAttr,Binder binder,Object [ ]参数,CultureInfo文化,布尔skipVisibilityChecks) 在System.Reflection.RuntimeMethodInfo。Invoke(Object obj,BindingFlags invokeAttr,Binder binder,Object []参数,CultureInfo culture) at System.RuntimeType.InvokeMember(String name,BindingFlags bindingFlags,Binder binder,Object target,Object [] providedArgs,ParameterModifier []修饰符,CultureInfo培养,字符串[] namedParams) 在System.Type.InvokeMember(字符串名称,的BindingFlags invokeAttr,粘结剂粘结剂,对象目标,在myProject的 .Presenter.CustomerDetailPresenter.RevertCustomer()

对象[]参数) 方法3 System.Reflection.TargetInvocationException:调用的目标引发了异常。 ---> System.NullReferenceException:未将对象引用设置为对象的实例。 在myProject的 .Presenter.CustomerDetailPresenter.set_City(字符串值) ---内部异常堆栈跟踪的结尾--- 在System.RuntimeMethodHandle._InvokeMethodFast(对象目标,对象[]参数,SignatureStruct & SIG,MethodAttributes methodAttributes ,运行时类型句柄typeOwner) at System.RuntimeMethodHandle.InvokeMethodFast(Object object,Object [] arguments,Signature sig,MethodAttributes methodAttributes,RuntimeTypeHandle typeOwner) at System.Reflection.RuntimeMethodInfo.Invoke(Object obj,BindingFlags invokeAttr,Binder binder,Object [ ]参数,CultureInfo culture,布尔skipVisibilityChecks)

at System.Ref lection.RuntimeMethodInfo.Invoke(Object obj,BindingFlags invokeAttr,Binder binder,Object []参数,CultureInfo culture) at System.RuntimeType.InvokeMember(String name,BindingFlags bindingFlags,Binder binder,Object target,Object [] providedArgs,ParameterModifier [ ]改性剂,CultureInfo的文化,字符串[] namedParams) 在System.Type.InvokeMember(字符串名称,的BindingFlags invokeAttr,粘结剂粘结剂,目标对象,对象[]参数) 在myProject的 .Presenter.CustomerDetailPresenter.RevertCustomer()

+0

你确定PropertyInfo.SetValue是抛出异常的方法吗? – jason 2009-01-23 00:14:00

+0

这或它的一个子方法,就是这一行。 – 2009-01-23 01:22:27

+0

检查异常堆栈跟踪;你应该能够辨别出抛出异常的方法。如果你无法弄清楚,请尝试单独调用Convert.ChangeType。我怀疑是该方法抛出异常。 – jason 2009-01-23 01:31:59

回答

0

SetValueConvert.ChangeType呼吁pair.ValueIConvertible方法,当然,当您试图打电话给他们上他们失败210实例。

检查pair.Value为空,并通过显式null如果是这种情况。

InvokeMember需要数组作为第5个参数。尝试:

Params(0) = pair.Value 
Me.GetType().InvokeMember(pair.Key, Reflection.BindingFlags.SetProperty, Nothing, Me, Params) 
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); 
      } 
     } 

,您可以设置属性和调用存储(),它们将被保存到字典。然后你可以改变它们并调用Recall(),它们将被恢复。至少对于字符串来说,它似乎起作用。这似乎是一个很好的东西放在基类。

2

,你是在第二和第三个选项堆栈看到这样一个事实痕迹

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

让我觉得有东西在你的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(字符串 值)

的事实set_City()setter方法是什么,是引发异常表明该方法被发现并成功调用。空(没有)值正按要求传入。所以,这个错误并不在反思之中,而在于被调用的属性设置器的结果。您可能已经尝试了这一点,但是在setter中设置了一个断点,或者将IDE设置为在所有托管的异常中断,以查看是否可以捕获实际原因?或者,储存财产信息的状态是什么?名称,类型和值全部同步?

希望这会有所帮助。