协变表达式<Func <TBase,bool >>

时间:2019-11-21 21:42:27

标签: c# covariance

我们有一些通用代码。我们希望有一种方法可以接受Expression,如下所示

public interface ISmallInterface
{
    // ...
}

public interface IExample<TBase>
{
    IQueryable<TBase> Where(Expression<Func<TBase, bool>> predicate);
}

public class SmallClass : ISmallInterface
{
    // ...
}

从这里我们有了一些基本的实现

public abstract class AlphaBase<TBase> : IExample<TBase>
{
    public IQueryable<TBase> Where(Expression<Func<TBase, bool>> predicate)
    {
        // ...
    }
}

在我们的核心逻辑中,我们使用它来构建组件。然后在此Gamma示例中,我们想要一个公开IExample<ISmallInterface>的方法或属性。

public class Beta : AlphaBase<SmallClass>
{
    // ...
}

public class Gamma
{
    public IExample<ISmallInterface> GetThing()
    {
        var b = new Beta();
        return b;
    }
}

但是,这会导致编译器错误。

  

无法将类型“ Beta”隐式转换为“ IExample ”。存在显式转换(您是否缺少演员表?)

更改IExample以使用协变类型参数可以解决此转换问题,但会破坏Where方法。

public interface IExample<out TBase>
{
    IQueryable<TBase> Where(Expression<Func<TBase, bool>> predicate);
}

给出编译器错误

  

无效方差:类型参数“ TBase”必须在“ IExample .Where(Expression >)”上始终有效。 “ TBase”是协变的。

在我们的例子中,我们只能使用Func个参数。

public interface IExample<out TBase>
{
    IQueryable<TBase> Where(Func<TBase, bool> predicate);
}

public abstract class AlphaBase<TBase> : IExample<TBase>
{
    public IQueryable<TBase> Where(Func<TBase, bool> predicate)
    {
        throw new NotImplementedException();
    }
}

这将编译并运行。但是,使用Expression<Func<TBase, bool>>会很方便。

是否有某种变通方法可以将Expression<Func<TBase, bool>>与协变类型一起使用?

(dotnet核心2.2,如果那很重要,我认为是C#7.3)

1 个答案:

答案 0 :(得分:0)

该解决方案非常简单,并且似乎遵循“ Linq方式”。

只需使用正确的签名创建扩展方法,然后在其中实现即可。现在界面中没有Where方法。按照上面的示例,代码类似于

public interface IExample<out TBase>
{
    // no Where, Count, or FirstOrDefault methods that accept Expression defined here

    // This is a lazy loading method and doesn't execute the query
    IQueryable<TBase> GetAll(ApplicationUser user);
}

public static class ExtensionMethods
{
    public static IQueryable<TBase> Where<TBase>(
        this IExample<TBase> repository,
        ApplicationUser user,
        Expression<Func<TBase, bool>> predicate)
    {
        return repository.GetAll(user).Where(predicate);
    }

    public static int Count<TBase>(
        this IExample<TBase> repository,
        ApplicationUser user,
        Expression<Func<TBase, bool>> predicate)
    {
        return repository.GetAll(user).Count(predicate);
    }

    public static TBase FirstOrDefault<TBase>(
        this IExample<TBase> repository,
        ApplicationUser user,
        Expression<Func<TBase, bool>> predicate)
    {
        return repository.GetAll(user).FirstOrDefault(predicate);
    }
}