C#泛型约束无法按预期工作

Ant*_*ott 6 c# generics

我有一种情况,我需要动态建立一个过滤器列表,以应用于对象列表.这些对象可以是实现包含我需要过滤的所有属性的接口的任何对象.

public interface IStuff
{
    bool SuitableForSomething { get; set; }
    bool SuitableForSomethingElse { get; set; }
}

public class SomeStuff : IStuff
{
    ...
}

public class SomeOtherStuff : IStuff
{
    ...
}
Run Code Online (Sandbox Code Playgroud)

我有一个如此定义的标准列表......

public List<Expression<Func<IStuff, bool>>> Criteria { get; private set; } 
Run Code Online (Sandbox Code Playgroud)

并添加这样的标准......

Criteria.Add(x => x.SuitableForSomething);
Criteria.Add(x => x.SuitableForSomethingElse);
Run Code Online (Sandbox Code Playgroud)

然后我将标准应用于我的查询,就像这样......

var stuff= _stuffCache
    .GetAll()
    .AsQueryable()
    .ApplyCriteria(Criteria);
Run Code Online (Sandbox Code Playgroud)

它使用以下扩展方法......

public static IQueryable<T> ApplyCriteria<T>(this IQueryable<T> stuff, List<Expression<Func<IStuff, bool>>> criteria) 
    where T : IStuff
{
    foreach (var expression in criteria)
    {
        stuff = Queryable.Where(stuff, expression);
    }
    return stuff;
}
Run Code Online (Sandbox Code Playgroud)

编译器告诉我......

cannot convert from
    'System.Linq.Expressions.Expression<System.Func<IStuff,bool>>'
to
    'System.Linq.Expressions.Expression<System.Func<T,int,bool>>'
Run Code Online (Sandbox Code Playgroud)

当我将鼠标悬停在IDE中的错误下的红线上时,它说它无法解决之间的方法

    IQueryable<IStuff> Where<IStuff>(this IQueryable<IStuff>, Expression<Func<IStuff, bool>>) in class Queryable
and
    IQueryable<T> Where<T>(this IQueryable<T>, Expression<Func<T,int,bool>>) in class Queryable
Run Code Online (Sandbox Code Playgroud)

如果我尝试将表达式转换为Expression<Func<T, bool>>,这应该起作用,因为T被约束为实现IStuff接口.我明白了

Cannot cast expression of type 'Expression<Func<IStuff, bool>>' to type 'Expression<Func<T, bool>>'
Run Code Online (Sandbox Code Playgroud)

编辑 感谢Raphaël的回答我修改了扩展方法并最终找到了我遇到的真正问题,这在我调用代码时是一个转换问题.通过.Cast<SomeStuff>()在ApplyCriteria通话后添加一个轻松修复.

之前

var stuff= _stuffCache
    .GetAll()
    .AsQueryable()
    .ApplyCriteria(Criteria);
Run Code Online (Sandbox Code Playgroud)

后

var stuff= _stuffCache
    .GetAll()
    .AsQueryable()
    .ApplyCriteria(Criteria)
    .Cast<SomeStuff>();
Run Code Online (Sandbox Code Playgroud)

Rap*_*aus 7

将第二个参数类型更改为List<Expression<Func<T,bool>>>(T而不是IStuff)

public static IQueryable<T> ApplyCriteria<T>(this IQueryable<T> stuff, List<Expression<Func<T, bool>>> criteria) 
    where T : IStuff
{
    foreach (var expression in criteria)
    {
        stuff = Queryable.Where(stuff, expression);
        //or stuff = stuff.Where(expression), as Where is an Extension method;
    }
    return stuff;
}
Run Code Online (Sandbox Code Playgroud)

并且你的方法可以(thx resharper),重写为

public static IQueryable<T> ApplyCriteria<T>(this IQueryable<T> stuff, List<Expression<Func<T, bool>>> criteria)
            where T : IStuff
        {
            return criteria.Aggregate(stuff, (current, expression) => current.Where(expression));
        }
Run Code Online (Sandbox Code Playgroud)