【问题标题】:Covariant Expression<Func<TBase, bool>>协变表达式<Func<TBase, bool>>
【发布时间】:2019-11-21 21:42:27
【问题描述】:

我们有一些通用代码。我们想要一个接受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&lt;ISmallInterface&gt; 的方法或属性。

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&lt;Func&lt;TBase, bool&gt;&gt; 会很方便。

是否有某种解决方法可以将Expression&lt;Func&lt;TBase, bool&gt;&gt; 与协变类型一起使用?

(dotnet core 2.2,如果这很重要,我认为是 C#7.3)

【问题讨论】:

  • 方差与类型推断大致相同。 (“变种”)。它必须在编译时知道并且 100% 确定它实际上是什么类型。使用 Variance,您可以让编译器自动添加必要的强制转换。所有这些推断的自动性都不存在于表达式树中,这必须手动完成。编译器将表达式的泛型解析为具体类型,但在协变的情况下,它不能说“它是这种类型”或“它是那种类型”。在某处进行了隐式转换。这必须在表达式中明确完成。这不是一个答案,也许是一个提示。

标签: c# covariance


【解决方案1】:

解决方案很简单,似乎遵循“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);
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-11
    相关资源
    最近更新 更多