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

发布于 2024-07-13 04:18:26 字数 3798 浏览 4 评论 0原文

编辑:根据 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 时,我都会收到空引用异常。 setter 能够保存空值(该属性通常是字符串)。 我做错了什么或者我该如何解决它?

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

编辑:以下是堆栈跟踪(如果它们对任何人有帮助):

方法 1 System.NullReferenceException:未将对象引用设置为对象的实例。 在 Microsoft.VisualBasic.CompilerServices.Symbols.Container.InvokeMethod(方法 TargetProcedure、Object[] 参数、Boolean[] CopyBack、BindingFlags 标志) 在 Microsoft.VisualBasic.CompilerServices.NewLateBinding.LateSet(对象实例,类型类型,字符串成员名称,对象 [] 参数,字符串 [] ArgumentNames,类型 [] TypeArguments,布尔 OptimisticSet,布尔 RValueBase,CallType CallType) 在 Microsoft.VisualBasic.CompilerServices.Versioned.CallByName(对象实例,字符串方法名称,CallType UseCallType,Object[] Arguments) myProject.Presenter.CustomerDetailPresenter.RevertCustomer() in myfile:第 378 行

方法 2 System.Reflection.TargetInitationException:调用目标已引发异常。 ---> System.NullReferenceException:未将对象引用设置为对象的实例。 在 myProject.Presenter.CustomerDetailPresenter.set_City(字符串值) --- 内部异常堆栈跟踪结束 --- 在 System.RuntimeMethodHandle._InvokeMethodFast(对象目标,Object[] 参数,SignatureStruct& sig,MethodAttributes methodAttributes,RuntimeTypeHandle typeOwner) 在 System.RuntimeMethodHandle.InvokeMethodFast(对象目标,Object[] 参数,签名 sig,MethodAttributes methodAttributes,RuntimeTypeHandle typeOwner) 在System.Reflection.RuntimeMethodInfo.Invoke(对象obj,BindingFlags invokeAttr,Binder活页夹,Object []参数,CultureInfo文化,布尔skipVisibilityChecks) 在System.Reflection.RuntimeMethodInfo.Invoke(对象obj,BindingFlags invokeAttr,Binder活页夹,Object []参数,CultureInfo文化) 在System.RuntimeType.InvokeMember(字符串名称,BindingFlags绑定Flags,活页夹活页夹,对象目标,对象[]提供的参数,ParameterModifier []修饰符,CultureInfo文化,字符串[]命名参数) 在 System.Type.InvokeMember(字符串名称,BindingFlags invokeAttr,Binder 绑定器,对象目标,Object[] args) 在 myProject.Presenter.CustomerDetailPresenter.RevertCustomer()

方法 3 System.Reflection.TargetInitationException:调用目标已引发异常。 ---> System.NullReferenceException:未将对象引用设置为对象的实例。 在 myProject.Presenter.CustomerDetailPresenter.set_City(字符串值) --- 内部异常堆栈跟踪结束 --- 在 System.RuntimeMethodHandle._InvokeMethodFast(对象目标,Object[] 参数,SignatureStruct& sig,MethodAttributes methodAttributes,RuntimeTypeHandle typeOwner) 在 System.RuntimeMethodHandle.InvokeMethodFast(对象目标,Object[] 参数,签名 sig,MethodAttributes methodAttributes,RuntimeTypeHandle typeOwner) 在System.Reflection.RuntimeMethodInfo.Invoke(对象obj,BindingFlags invokeAttr,Binder活页夹,Object []参数,CultureInfo文化,布尔skipVisibilityChecks)

在System.Reflection.RuntimeMethodInfo.Invoke(对象obj,BindingFlags invokeAttr,Binder活页夹,Object []参数、CultureInfo 文化) 在System.RuntimeType.InvokeMember(字符串名称,BindingFlags绑定Flags,活页夹活页夹,对象目标,对象[]提供的参数,ParameterModifier []修饰符,CultureInfo文化,字符串[]命名参数) 在 System.Type.InvokeMember(字符串名称,BindingFlags invokeAttr,Binder 绑定器,对象目标,Object[] args) 在 myProject.Presenter.CustomerDetailPresenter.RevertCustomer()

Edit: Based on the answer from LoveMeSomeCode, I believe this issue only appears in VB.Net.

I'm trying to revert a class to a previous state by saving the old values of changed properties in a dictionary and setting them via reflection when I need to revert. I'm having a problem where if the old value is Nothing (null) I get a null reference exception when trying to set the property. Here's what I've tried.

Assume a for each loop like this:

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

Method 1:

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

Method 2:

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

Method 3:

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

For each of these methods I get a null reference exception when pair.Value is null. The setter is capable of holding a null value (frequently the property is a string). What am I doing wrong or how can I work around it?

Edit: Each method fails if I pass it null directly as well.

Edit: Here are the stack traces if they help anyone:

Method 1
System.NullReferenceException: Object reference not set to an instance of an object.
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[] 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.Presenter.CustomerDetailPresenter.RevertCustomer() in myfile:line 378

Method 2
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.NullReferenceException: Object reference not set to an instance of an object.
at myProject.Presenter.CustomerDetailPresenter.set_City(String value)
--- End of inner exception stack trace ---
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[] parameters, CultureInfo culture)
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)
at myProject.Presenter.CustomerDetailPresenter.RevertCustomer()

Method 3
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.NullReferenceException: Object reference not set to an instance of an object.
at myProject.Presenter.CustomerDetailPresenter.set_City(String value)
--- End of inner exception stack trace ---
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[] parameters, CultureInfo culture)
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)
at myProject.Presenter.CustomerDetailPresenter.RevertCustomer()

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

忆悲凉 2024-07-20 04:18:26

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

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

让我认为您的 CustomerDetailPresenter.City 属性设置器中有些内容没有处理 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:对象
未设置对实例的引用
目的。 在
myProject.Presenter.CustomerDetailPresenter.set_City(字符串
值)

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

希望这可以帮助。

The fact that you are seeing this in the 2nd and 3rd option stack traces

System.NullReferenceException: Object
reference not set to an instance of an
object. at
myProject.Presenter.CustomerDetailPresenter.set_City(String
value)

makes me think that there is something in your CustomerDetailPresenter.City property setter that is not handling the null value. What is the implementation of your property setter? Is that any validation or auditing code that could be failing?

Update 03-24-2009:
A quick test in VB, and this code works as intended. I tried to capture the scenario you describe.

My test class that has its property set (in part):

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

I have created a PropertyState class that will hold the property name, value and type. And the code to set the property dynamically is:

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

I used two different overloads of the SetValue method, I have found that not explicity setting the BindingFlags can cause reflection issues at times. However, in this case, both overlaods work fine.

So, I look back to the stack trace you posted in your question:

System.NullReferenceException: Object
reference not set to an instance of an
object. at
myProject.Presenter.CustomerDetailPresenter.set_City(String
value)

the fact that the set_City() setter is what is throwing the exception indicates that the method is being found and called successfully. The null(nothing) value is being passed in as requested. So, the bug is not in the reflection but in what is happening as a result of the property setter being called. You probably have already tried this, but setting a break point in the setter or setting the IDE to break on all managed exceptions to see if you can capture the actual cause? Or, is the state of the stored property info what is expected? Name, type and value all in sync?

Hope this helps.

好多鱼好多余 2024-07-20 04:18:26

SetValue 中,Convert.ChangeTypepair.Value 上调用 IConvertible 方法,当您尝试调用时它们肯定会失败它们位于 null 实例上。

检查pair.Value是否为空,如果是则传递显式的null。

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

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

In SetValue, Convert.ChangeType calls IConvertible methods on pair.Value, surely they fail when you try to call them on null instance.

Check pair.Value for nullness and pass an explicit null if it is the case.

InvokeMember expects an array as 5th argument. Try:

Params(0) = pair.Value
Me.GetType().InvokeMember(pair.Key, Reflection.BindingFlags.SetProperty, Nothing, Me, Params)
陈年往事 2024-07-20 04:18:26

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

Well, this is C# instead of VB.NET, but this seems to work:

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);
            }
        }

so you can set the properties and call Store(), and they will be saved to the dictionary. Then you can change them and call Recall() and they will be restored. It seems to work will null for strings at least. It seems like a good set of things to put in a base class.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文