Я знаю как fix a nullrefence, но в этом случае он находит/фиксирует его в дереве выражений.Как обрабатывать null valuetype в дереве выражений, чтобы он не дал nullreference?
Я не достаточно знаком с деревом выражений, чтобы сделать это сам, чтобы кто-то мог обучить меня этому?
Этот код будет работать с первым свойством (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
используя ответ от Shlomo, я создал рабочее решение для .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
Есть ли способ изменить дерево выражений для обработки нулевого значения вместо изменения кода, который его вызывает? – Fredou
Обновлен ответ. – Shlomo
Я переместил его в рабочее решение для .net 3.5, похоже, работает – Fredou