我希望支持“ expression passthru”,如下所示:
public IFastIndexingCollection<T> {
// Main data structure here =
// Key = property name
// Value = class below
private Dictionary<string, ICustomCollection<T, U>> _indexedItems;
public IEnumerable<T> FindWhere(Expression<Func<T, bool>> expression);
}
internal ICustomCollection<T, U> {
// Main data structure =
// T = type of data structure, e.g., Phone
// U = property type, e.g., string
// Key = value for given property (e.g., "Samsung")
// Value = List<T> of items matching that key
private ILookup<U, T> _backingLookup;
}
当我们试图将LINQ表达式传递到自定义列表时,麻烦就来了。假设用户执行:
<T> = Phone
FastIndexingCollection<Phone>.FindWhere(x => x.Manufacturer.IndexOf("Samsung") > -1);
在这种情况下,代码必须:
key = "Manufacturer"
的字典中拉出ICustomCollection
字典值可以实际识别; typeof(Manufacturer) = U = string
。因此,在我的x.Manufacturer.IndexOf(...)
示例中,“转换后的”表达式实际上不再需要x.Manufacturer
,因为该表达式未存储在查找中。ICustomCollection
正在使用的查询上执行LINQ表达式我从最上面的表达式中拉出了表达式主体,以使其脱离MethodInfo,并且可以找到正确的字典键值对,但是我不知道如何转换LINQ表达式,因此我可以将其应用于“最低”级别_backingLookup
:我尝试执行以下操作:
foreach(var kvp in _backingLookup)
{
if(...need to apply LINQ expression here... == true)
{
// Add _internalLookup[kvp.Key] to return value
}
}
我只是不知道如何在if中指出的地方应用LINQ表达式。想法?
答案 0 :(得分:1)
使用名为ExpressionVisitor
的通用Replace
,一旦获得了MemberExpression
即可获取属性或字段名称,就可以转换测试。
public static ExpressionExt {
/// <summary>
/// Replaces an Expression (reference Equals) with another Expression
/// </summary>
/// <param name="orig">The original Expression.</param>
/// <param name="from">The from Expression.</param>
/// <param name="to">The to Expression.</param>
/// <returns>Expression with all occurrences of from replaced with to</returns>
public static Expression Replace(this Expression orig, Expression from, Expression to) => new ReplaceVisitor(from, to).Visit(orig);
}
/// <summary>
/// ExpressionVisitor to replace an Expression (that is Equals) with another Expression.
/// </summary>
public class ReplaceVisitor : ExpressionVisitor {
readonly Expression from;
readonly Expression to;
public ReplaceVisitor(Expression from, Expression to) {
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node) => node == from ? to : base.Visit(node);
}
以下是FindWhere
实现的开始,该实现演示了如何使用Replace
:
public override IEnumerable<T> FindWhere(Expression<Func<T, bool>> testFn) {
var testBody = (BinaryExpression)testFn.Body;
var fldTestExpr = testBody.Left;
if (fldTestExpr.NodeType == ExpressionType.Call)
fldTestExpr = ((MethodCallExpression)fldTestExpr).Object;
if (fldTestExpr is MemberExpression me) {
var memberName = me.Member.Name;
var newp = Expression.Parameter(me.Type);
var newBody = testBody.Replace(me, newp);
var newLambda = Expression.Lambda(newBody, newp);
var newTestFn = newLambda.Compile();
var testans = (bool) newTestFn.DynamicInvoke("this Samsung that");
// using DynamicInvoke is not terrible efficient, but lacking a static
// type for the property means the compiler must use object
}
}
您可以使用FindWhere
版本来提高性能,该版本将成员访问权限与测试分开:
public override IEnumerable<T> FindWhere2<U>(Expression<Func<T, U>> accessExpr, Func<U, bool> testFn);
var ans = fic.FindWhere2(x => x.Manufacturer, y => y.IndexOf("Samsung") > -1);