Skip to content

Instantly share code, notes, and snippets.

@Ringes
Last active September 23, 2020 06:27
Show Gist options
  • Save Ringes/ff7d35dd39afc75e0abac24c74b55845 to your computer and use it in GitHub Desktop.
Save Ringes/ff7d35dd39afc75e0abac24c74b55845 to your computer and use it in GitHub Desktop.
This code is a direct port into VB.Net of the Universal PredicateBuilder described here: https://petemontgomery.wordpress.com/2011/02/10/a-universal-predicatebuilder/
Imports System.Linq.Expressions
Public Module PredicateBuilder
'Private Sub New()
'End Sub
''' <summary>
''' Creates a predicate that evaluates to true
''' </summary>
Public Function [True](Of T)() As Expression(Of Func(Of T, Boolean))
Return Function(f) True
End Function
''' <summary>
''' Creates a predicate that evaluates to false
''' </summary>
Public Function [False](Of T)() As Expression(Of Func(Of T, Boolean))
Return Function(f) False
End Function
''' <summary>
''' Creates a predicate expression from the specified lambda expression
''' </summary>
Public Function Create(Of T)(predicate As Expression(Of Func(Of T, Boolean))) As Expression(Of Func(Of T, Boolean))
Return predicate
End Function
''' <summary>
''' Combines the first predicate with the second using the logical "and".
''' </summary>
<System.Runtime.CompilerServices.Extension()>
Public Function [And](Of T)(ByVal first As Expression(Of Func(Of T, Boolean)), second As Expression(Of Func(Of T, Boolean))) As Expression(Of Func(Of T, Boolean))
Return first.Compose(second, AddressOf Expression.And)
End Function
''' <summary>
''' Combines the first predicate with the second using the logical "or".
''' </summary>
<System.Runtime.CompilerServices.Extension()>
Public Function [Or](Of T)(ByVal first As Expression(Of Func(Of T, Boolean)), second As Expression(Of Func(Of T, Boolean))) As Expression(Of Func(Of T, Boolean))
Return first.Compose(second, AddressOf Expression.Or)
End Function
''' <summary>
''' Negates the predicate.
''' </summary>
<System.Runtime.CompilerServices.Extension()>
Public Function [Not](Of T)(ByVal first As Expression(Of Func(Of T, Boolean))) As Expression(Of Func(Of T, Boolean))
Dim negated = Expression.Not(first.Body)
Return Expression.Lambda(Of Func(Of T, Boolean))(negated, first.Parameters)
End Function
''' <summary>
''' Combines the first expression with the second using the specified merge function.
''' </summary>
<System.Runtime.CompilerServices.Extension()>
Public Function Compose(Of T)(ByVal first As Expression(Of T), second As Expression(Of T), merge As Func(Of Expression, Expression, Expression)) As Expression(Of T)
'zip parameters (map from parameters of second to parameters of first)
Dim map = first.Parameters _
.Select(Function(f, i) New With {Key f, Key .s = second.Parameters(i)}) _
.ToDictionary(Function(p) p.s, Function(p) p.f)
'replace parameters in the second lambda expression with the parameters in the first
Dim secondBody = ParameterRebinder.ReplaceParameters(map, second.Body)
'create a merged lambda expression with parameters from the first expression
Return Expression.Lambda(Of T)(merge(first.Body, secondBody), first.Parameters)
End Function
End Module
Class ParameterRebinder
Inherits ExpressionVisitor
ReadOnly _map As Dictionary(Of ParameterExpression, ParameterExpression)
Sub New(map As Dictionary(Of ParameterExpression, ParameterExpression))
If map Is Nothing Then
_map = New Dictionary(Of ParameterExpression, ParameterExpression)
Else
_map = map
End If
End Sub
Public Shared Function ReplaceParameters(map As Dictionary(Of ParameterExpression, ParameterExpression), exp As Expression) As Expression
Return New ParameterRebinder(map).Visit(exp)
End Function
Protected Overrides Function VisitParameter(node As ParameterExpression) As Expression
Dim replacement As ParameterExpression
If _map.TryGetValue(node, replacement) Then
node = replacement
End If
Return MyBase.VisitParameter(node)
End Function
End Class
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment