EDIT
Я получаю исключение каждый раз, когда пытаюсь создать выражение LINQ из метода, который содержит вызов локально определенного делегата (или лямбды) или функции в классе или модуле.
У меня есть функция, которая принимает два параметра (делегат и целое число), а затем создает LINQ MethodCallExpression
, который она использует для получения результатов:
Public Delegate Function CompareTwoIntegerFunction(ByVal i1 As Integer, ByVal i2 As Integer) As Boolean
Public Function Test(ByVal pFunc As CompareTwoIntegerFunction, ByVal i1 As Integer, ByVal i2 As Integer) As Boolean
Dim lParamExpression As ParameterExpression = Expression.Parameter(GetType(Integer), "i")
Dim lConstExpr As ConstantExpression = Expression.Constant(i1, GetType(Integer))
Dim lMatcher As CompareTwoIntegerFunction = pFunc
' *** This line throws the exception (line 17)
Dim lMatcherExpr As MethodCallExpression = Expression.Call(lMatcher.Method, lParamExpression, lConstExpr)
' Now use the expression and get the result
Dim lFunc As Func(Of Integer, Boolean) = (Expression.Lambda(Of Func(Of Integer, Boolean))(lMatcherExpr, lParamExpression)).Compile
Return lFunc(i2)
End Function
Я могу запустить этот код без ошибок в этом тесте:
Dim lMatchedPass1 As Boolean = Test(Function(a, b) a = b, 10, 10)
Я даже могу придумать и сделать что-то вроде этого:
Dim lMatchedPass2 As Boolean = Test(Function(a, b) (Function(c As Integer) c + 1)(a) = b, 10, 9)
Однако, если я попробую что-то подобное, я получу исключение:
Dim ChildFunc As Func(Of Integer, Integer) = Function(s As Integer) s + 1
Dim MatchedFail1 As Boolean = Test(Function(a, b) (ChildFunc(a)) = b, 10, 9)
Сообщение об исключении:
Exception: [2011 Jun 23 (Thu) 10:25:29 AM] [ System.Core ]
Value cannot be null.
Parameter name: instance
at System.Linq.Expressions.Expression.ValidateCallArgs(Expression instance, MethodInfo method, ReadOnlyCollection`1& arguments)
at System.Linq.Expressions.Expression.Call(Expression instance, MethodInfo method, IEnumerable`1 arguments)
at System.Linq.Expressions.Expression.Call(MethodInfo method, Expression[] arguments)
at DataManager`1.Test(CompareTwoIntegerFunction pFunc, Int32 i1, Int32 i2) in DataManager.vb:line 17
at DataManager`1..ctor() in DataManager.vb:line 27
Called from: Void ValidateCallArgs(System.Linq.Expressions.Expression, System.Reflection.MethodInfo, System.Collections.ObjectModel.ReadOnlyCollection`1[System.Linq.Expressions.Expression] ByRef)
Я хочу понять, почему MatchTest2
нормально, но MatchFail1
- это проблема.
Примечание 1:
В приведенном выше примере используются константа и параметр, но я получаю одинаковые результаты с двумя константами или двумя параметрами. Я также могу получить исключение, заменив именованный тип делегата на определение анонимной функции.
Примечание 2:
Я безуспешно пытался добавить Nothing
в качестве параметра экземпляра в строку Expression.Call
, например:
Dim lMatcherExpr As MethodCallExpression = Expression.Call(Nothing, pFunc.Method, lParamExpression, lConstExpr)
Примечание 3:
Я обнаружил, что могу «исправить» проблему в приведенном выше коде, переместив мою «дочернюю функцию» в модуль или превратив ее в «разделяемую» функцию в моем классе. Это не очень реалистичное решение, хотя в нетривиальном случае, потому что я хочу иметь возможность передавать дочернюю функцию в качестве параметра.
Другими словами, моя цель - сделать что-то вроде этого:
Public Function RunTestWithChildFunction(ByVal a As Integer, ByVal b As Integer, ByVal ChildFunc As Func(Of Integer, Integer, Boolean)) As Boolean
Return Test(Function(x, y) ChildFunc(x, y), a, b)
End Function
Public Function EqualityCheck(ByVal a As Integer, ByVal b As Integer) As Boolean
Return a = b
End Function
Public Sub DoTest
Dim lMatchedFail2 As Boolean = RunTestWithChildFunction(10, 10, AddressOf EqualityCheck)
End
Добавление «shared» в EqualityCheck
в этом коде не решает проблему, потому что адрес передается ByVal
в RunCheckWithChildFunction
(я получаю ошибку компиляции, если я пытаюсь передать его ByRef
).