如何处理表达式树中的空值类型以使其不提供空引用?
how to handle null valuetype in expression tree so it wont give nullreference?
我知道如何 fix a nullrefence 但在这种情况下它是 finding/fixing 它在表达式树中。
我对表达式树不够熟悉,无法自己完成,所以有人可以用这个来教育我吗?
此代码适用于第一个 属性(Prop1
) 但不适用于第二个 (Prop4
)
Option Strict On
Option Explicit On
Imports System.Linq.Expressions
Imports System.Reflection
Imports System.Runtime.CompilerServices
Module Module1
Const loopRun As Integer = 25000
Const benchRun As Integer = 5
Private myObj As New Obj With {.Prop1 = "hello",
.Prop4 = 123,
.Prop7 = Now,
.Prop10 = Obj.test.value2}
Sub Main()
DisplayValue()
Console.Read()
End Sub
Private Sub DisplayValue()
Dim value As Object
For Each i In Cache.expressionGetDict
value = i.Value(myObj)
Console.WriteLine("Original expressionGetDict.{0}={1}", i.Key, i.Value(myObj))
Cache.expressionSetDict(i.Key)(myObj, Nothing) ''on Prop4, null reference
Console.WriteLine("Cleared expressionGetDict.{0}={1}", i.Key, i.Value(myObj))
Cache.expressionSetDict(i.Key)(myObj, value)
Console.WriteLine("Old expressionGetDict.{0}={1}", i.Key, i.Value(myObj))
Console.WriteLine()
Next
End Sub
End Module
Public Class Obj
Public Enum test As Byte
value1 = 10
value2 = 50
value3 = 250
End Enum
Public Property Prop1 As String
Public Property Prop4 As Integer
Public Property Prop7 As DateTime
Public Property Prop10 As test
End Class
Public Module Cache
Public ReadOnly expressionGetDict As New Dictionary(Of String, Func(Of Object, Object))
Public ReadOnly expressionSetDict As New Dictionary(Of String, Action(Of Object, Object))
Sub New()
For Each p In GetType(Obj).GetProperties(BindingFlags.Instance Or BindingFlags.[Public])
expressionGetDict.Add(p.Name, p.GetValueGetter)
expressionSetDict.Add(p.Name, p.GetValueSetter)
Next
End Sub
End Module
Public Module PropertyInfoExtensions
<Extension> _
Public Function GetValueGetter(propertyInfo As PropertyInfo) As Func(Of Object, Object)
Dim instance As ParameterExpression = Expression.Parameter(GetType(Object), "instance")
Dim instanceCast As UnaryExpression = If(Not propertyInfo.DeclaringType.IsValueType, Expression.TypeAs(instance, propertyInfo.DeclaringType), Expression.Convert(instance, propertyInfo.DeclaringType))
Dim getterCall As MethodCallExpression = Expression.[Call](instanceCast, propertyInfo.GetGetMethod())
Dim convert As UnaryExpression = Expression.TypeAs(getterCall, GetType(Object))
Dim lambda As Expression(Of Func(Of Object, Object)) = Expression.Lambda(Of Func(Of Object, Object))(convert, instance)
Return lambda.Compile
End Function
<Extension> _
Public Function GetValueSetter(propertyInfo As PropertyInfo) As Action(Of Object, Object)
Dim instance As ParameterExpression = Expression.Parameter(GetType(Object), "instance")
Dim value As ParameterExpression = Expression.Parameter(GetType(Object), "value")
Dim instanceCast As UnaryExpression = If((Not propertyInfo.DeclaringType.IsValueType), Expression.TypeAs(instance, propertyInfo.DeclaringType), Expression.Convert(instance, propertyInfo.DeclaringType))
Dim valueCast As UnaryExpression = If((Not propertyInfo.PropertyType.IsValueType), Expression.TypeAs(value, propertyInfo.PropertyType), Expression.Convert(value, propertyInfo.PropertyType))
Dim setterCall As MethodCallExpression = Expression.[Call](instanceCast, propertyInfo.GetSetMethod(), valueCast)
Dim lambda As Expression(Of Action(Of Object, Object)) = Expression.Lambda(Of Action(Of Object, Object))(setterCall, instance, value)
Return lambda.Compile()
End Function
End Module
使用 的答案,我为 .net 3.5 创建了一个可行的解决方案
Public Function GetValueSetter(propertyInfo As PropertyInfo) As Action(Of Object, Object)
Dim instance As ParameterExpression = Expression.Parameter(GetType(Object), "instance")
Dim value As ParameterExpression = Expression.Parameter(GetType(Object), "value")
Dim nullCheckedValue = Expression.Condition(
Expression.Equal(value, Expression.Constant(Nothing, GetType(Object))),
Expression.Convert(GetDefaultExpression(propertyInfo.PropertyType), GetType(Object)),
value
)
Dim instanceCast As UnaryExpression = Expression.Convert(instance, propertyInfo.DeclaringType)
Dim valueCast As UnaryExpression = Expression.Convert(nullCheckedValue, propertyInfo.PropertyType)
Dim setterCall As MethodCallExpression = Expression.[Call](instanceCast, propertyInfo.GetSetMethod(), valueCast)
Dim lambda As Expression(Of Action(Of Object, Object)) = Expression.Lambda(Of Action(Of Object, Object))(setterCall, instance, value)
Return lambda.Compile
End Function
Private Function GetDefaultExpression(type As Type) As Expression
If type.IsValueType Then
Return Expression.Constant(Activator.CreateInstance(type), GetType(Object))
End If
Return Expression.Constant(Nothing, GetType(Object))
End Function
据我所知,这里有些事情很奇怪。
GetValueGetter 中的这一行 Dim convert As UnaryExpression = Expression.TypeAs(getterCall, GetType(Object))
应该是 Expression.Convert
,而不是 TypeAs
。 TypeAs
仅适用于引用类型,四个属性中的三个是值类型。但是,这似乎不是您当前的错误。
同样,VB.NET 的 Nothing
也让您感到困惑。 VB.NET 在编译时编译 Nothing
。由于您动态生成的函数的类型为 Object
,因此 Nothing
赋值尝试将 Object
的 Nothing(即 null
引用)赋值给 Prop4
.由于 Prop4
是值类型,因此您会得到空引用异常。您想要 Integer
的 Nothing 分配给 Prop4
.
经过以下修改,我的代码可以正常工作:
像这样设置模块缓存:
Public Module Cache
Public ReadOnly expressionGetDict As New Dictionary(Of String, Func(Of Object, Object))
Public ReadOnly expressionSetDict As New Dictionary(Of String, Action(Of Object, Object))
Public ReadOnly propertyTypeDict As New Dictionary(Of String, Type)
Sub New()
For Each p In GetType(Obj).GetProperties(BindingFlags.Instance Or BindingFlags.[Public])
expressionGetDict.Add(p.Name, p.GetValueGetter.Compile())
expressionSetDict.Add(p.Name, p.GetValueSetter.Compile())
propertyTypeDict(p.Name) = p.PropertyType
Next
End Sub
End Module
替换了 Cache.expressionSetDict(i.Key)(myObj, Nothing)
(在 DisplayValue
中:
Dim propertyType = Cache.propertyTypeDict(i.Key)
Dim typedNothing = CTypeDynamic(Nothing, propertyType)
Cache.expressionSetDict(i.Key)(myObj, typedNothing) 'on Prop4, no longer a null reference exception
编辑:
这个问题在表达式构建中也是可以解决的。您可以相应地修改 GetValueSetter
而不是执行上述操作:
Public Function GetValueSetter(propertyInfo As PropertyInfo) As Expression(Of Action(Of Object, Object))
Dim instance As ParameterExpression = Expression.Parameter(GetType(Object), "instance")
Dim value As ParameterExpression = Expression.Parameter(GetType(Object), "value")
Dim nullCheckedValue = Expression.Condition(
Expression.ReferenceEqual(value, Expression.Default(GetType(Object))),
Expression.Convert(Expression.Constant(CTypeDynamic(Nothing, propertyInfo.PropertyType)), GetType(Object)),
value
)
Dim instanceCast As UnaryExpression = Expression.Convert(instance, propertyInfo.DeclaringType)
Dim valueCast As UnaryExpression = Expression.Convert(nullCheckedValue, propertyInfo.PropertyType)
Dim setterCall As MethodCallExpression = Expression.[Call](instanceCast, propertyInfo.GetSetMethod(), valueCast)
Dim lambda As Expression(Of Action(Of Object, Object)) = Expression.Lambda(Of Action(Of Object, Object))(setterCall, instance, value)
Return lambda
End Function
第二个解决方案在表达式生成的函数中嵌入了一个空值检查,并用 default(T)
替换了一个空值,正如 C-Sharpists 所说的那样。用 VB 的说法我猜你会说你正在用正确的 Nothing
.
替换错误的 Nothing
我知道如何 fix a nullrefence 但在这种情况下它是 finding/fixing 它在表达式树中。
我对表达式树不够熟悉,无法自己完成,所以有人可以用这个来教育我吗?
此代码适用于第一个 属性(Prop1
) 但不适用于第二个 (Prop4
)
Option Strict On
Option Explicit On
Imports System.Linq.Expressions
Imports System.Reflection
Imports System.Runtime.CompilerServices
Module Module1
Const loopRun As Integer = 25000
Const benchRun As Integer = 5
Private myObj As New Obj With {.Prop1 = "hello",
.Prop4 = 123,
.Prop7 = Now,
.Prop10 = Obj.test.value2}
Sub Main()
DisplayValue()
Console.Read()
End Sub
Private Sub DisplayValue()
Dim value As Object
For Each i In Cache.expressionGetDict
value = i.Value(myObj)
Console.WriteLine("Original expressionGetDict.{0}={1}", i.Key, i.Value(myObj))
Cache.expressionSetDict(i.Key)(myObj, Nothing) ''on Prop4, null reference
Console.WriteLine("Cleared expressionGetDict.{0}={1}", i.Key, i.Value(myObj))
Cache.expressionSetDict(i.Key)(myObj, value)
Console.WriteLine("Old expressionGetDict.{0}={1}", i.Key, i.Value(myObj))
Console.WriteLine()
Next
End Sub
End Module
Public Class Obj
Public Enum test As Byte
value1 = 10
value2 = 50
value3 = 250
End Enum
Public Property Prop1 As String
Public Property Prop4 As Integer
Public Property Prop7 As DateTime
Public Property Prop10 As test
End Class
Public Module Cache
Public ReadOnly expressionGetDict As New Dictionary(Of String, Func(Of Object, Object))
Public ReadOnly expressionSetDict As New Dictionary(Of String, Action(Of Object, Object))
Sub New()
For Each p In GetType(Obj).GetProperties(BindingFlags.Instance Or BindingFlags.[Public])
expressionGetDict.Add(p.Name, p.GetValueGetter)
expressionSetDict.Add(p.Name, p.GetValueSetter)
Next
End Sub
End Module
Public Module PropertyInfoExtensions
<Extension> _
Public Function GetValueGetter(propertyInfo As PropertyInfo) As Func(Of Object, Object)
Dim instance As ParameterExpression = Expression.Parameter(GetType(Object), "instance")
Dim instanceCast As UnaryExpression = If(Not propertyInfo.DeclaringType.IsValueType, Expression.TypeAs(instance, propertyInfo.DeclaringType), Expression.Convert(instance, propertyInfo.DeclaringType))
Dim getterCall As MethodCallExpression = Expression.[Call](instanceCast, propertyInfo.GetGetMethod())
Dim convert As UnaryExpression = Expression.TypeAs(getterCall, GetType(Object))
Dim lambda As Expression(Of Func(Of Object, Object)) = Expression.Lambda(Of Func(Of Object, Object))(convert, instance)
Return lambda.Compile
End Function
<Extension> _
Public Function GetValueSetter(propertyInfo As PropertyInfo) As Action(Of Object, Object)
Dim instance As ParameterExpression = Expression.Parameter(GetType(Object), "instance")
Dim value As ParameterExpression = Expression.Parameter(GetType(Object), "value")
Dim instanceCast As UnaryExpression = If((Not propertyInfo.DeclaringType.IsValueType), Expression.TypeAs(instance, propertyInfo.DeclaringType), Expression.Convert(instance, propertyInfo.DeclaringType))
Dim valueCast As UnaryExpression = If((Not propertyInfo.PropertyType.IsValueType), Expression.TypeAs(value, propertyInfo.PropertyType), Expression.Convert(value, propertyInfo.PropertyType))
Dim setterCall As MethodCallExpression = Expression.[Call](instanceCast, propertyInfo.GetSetMethod(), valueCast)
Dim lambda As Expression(Of Action(Of Object, Object)) = Expression.Lambda(Of Action(Of Object, Object))(setterCall, instance, value)
Return lambda.Compile()
End Function
End Module
使用
Public Function GetValueSetter(propertyInfo As PropertyInfo) As Action(Of Object, Object)
Dim instance As ParameterExpression = Expression.Parameter(GetType(Object), "instance")
Dim value As ParameterExpression = Expression.Parameter(GetType(Object), "value")
Dim nullCheckedValue = Expression.Condition(
Expression.Equal(value, Expression.Constant(Nothing, GetType(Object))),
Expression.Convert(GetDefaultExpression(propertyInfo.PropertyType), GetType(Object)),
value
)
Dim instanceCast As UnaryExpression = Expression.Convert(instance, propertyInfo.DeclaringType)
Dim valueCast As UnaryExpression = Expression.Convert(nullCheckedValue, propertyInfo.PropertyType)
Dim setterCall As MethodCallExpression = Expression.[Call](instanceCast, propertyInfo.GetSetMethod(), valueCast)
Dim lambda As Expression(Of Action(Of Object, Object)) = Expression.Lambda(Of Action(Of Object, Object))(setterCall, instance, value)
Return lambda.Compile
End Function
Private Function GetDefaultExpression(type As Type) As Expression
If type.IsValueType Then
Return Expression.Constant(Activator.CreateInstance(type), GetType(Object))
End If
Return Expression.Constant(Nothing, GetType(Object))
End Function
据我所知,这里有些事情很奇怪。
GetValueGetter 中的这一行
Dim convert As UnaryExpression = Expression.TypeAs(getterCall, GetType(Object))
应该是Expression.Convert
,而不是TypeAs
。TypeAs
仅适用于引用类型,四个属性中的三个是值类型。但是,这似乎不是您当前的错误。同样,VB.NET 的
Nothing
也让您感到困惑。 VB.NET 在编译时编译Nothing
。由于您动态生成的函数的类型为Object
,因此Nothing
赋值尝试将Object
的 Nothing(即null
引用)赋值给Prop4
.由于Prop4
是值类型,因此您会得到空引用异常。您想要Integer
的 Nothing 分配给Prop4
.
经过以下修改,我的代码可以正常工作:
像这样设置模块缓存:
Public Module Cache
Public ReadOnly expressionGetDict As New Dictionary(Of String, Func(Of Object, Object))
Public ReadOnly expressionSetDict As New Dictionary(Of String, Action(Of Object, Object))
Public ReadOnly propertyTypeDict As New Dictionary(Of String, Type)
Sub New()
For Each p In GetType(Obj).GetProperties(BindingFlags.Instance Or BindingFlags.[Public])
expressionGetDict.Add(p.Name, p.GetValueGetter.Compile())
expressionSetDict.Add(p.Name, p.GetValueSetter.Compile())
propertyTypeDict(p.Name) = p.PropertyType
Next
End Sub
End Module
替换了 Cache.expressionSetDict(i.Key)(myObj, Nothing)
(在 DisplayValue
中:
Dim propertyType = Cache.propertyTypeDict(i.Key)
Dim typedNothing = CTypeDynamic(Nothing, propertyType)
Cache.expressionSetDict(i.Key)(myObj, typedNothing) 'on Prop4, no longer a null reference exception
编辑:
这个问题在表达式构建中也是可以解决的。您可以相应地修改 GetValueSetter
而不是执行上述操作:
Public Function GetValueSetter(propertyInfo As PropertyInfo) As Expression(Of Action(Of Object, Object))
Dim instance As ParameterExpression = Expression.Parameter(GetType(Object), "instance")
Dim value As ParameterExpression = Expression.Parameter(GetType(Object), "value")
Dim nullCheckedValue = Expression.Condition(
Expression.ReferenceEqual(value, Expression.Default(GetType(Object))),
Expression.Convert(Expression.Constant(CTypeDynamic(Nothing, propertyInfo.PropertyType)), GetType(Object)),
value
)
Dim instanceCast As UnaryExpression = Expression.Convert(instance, propertyInfo.DeclaringType)
Dim valueCast As UnaryExpression = Expression.Convert(nullCheckedValue, propertyInfo.PropertyType)
Dim setterCall As MethodCallExpression = Expression.[Call](instanceCast, propertyInfo.GetSetMethod(), valueCast)
Dim lambda As Expression(Of Action(Of Object, Object)) = Expression.Lambda(Of Action(Of Object, Object))(setterCall, instance, value)
Return lambda
End Function
第二个解决方案在表达式生成的函数中嵌入了一个空值检查,并用 default(T)
替换了一个空值,正如 C-Sharpists 所说的那样。用 VB 的说法我猜你会说你正在用正确的 Nothing
.
Nothing