2009-01-23 45 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設置爲在所有託管的異常中斷,以查看是否可以捕獲實際原因?或者,儲存財產信息的狀態是什麼?名稱,類型和值全部同步?

希望這會有所幫助。