协变表达式<Func<TBase, bool>>
Covariant Expression<Func<TBase, bool>>
我们有一些共同的代码。我们想要一个接受 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;
}
}
但是,这会产生编译错误。
Cannot implicitly convert type 'Beta' to 'IExample<ISmallInterface>'. An explicit conversion exists (are you missing a cast?)
将 IExample
更改为使用协变类型参数修复了此转换问题,但破坏了 Where
方法。
public interface IExample<out TBase>
{
IQueryable<TBase> Where(Expression<Func<TBase, bool>> predicate);
}
给出编译器错误
Invalid variance: The type parameter 'TBase' must be invariantly valid on 'IExample<TBase>.Where(Expression<Func<TBase, bool>>)'. 'TBase' is covariant.
在我们的案例中,我们只能使用 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 core 2.2,如果重要的话,我认为是 C#7.3)
解决方案很简单,似乎遵循 "the Linq way."
只需创建一个具有正确签名的扩展方法并在那里实现它。现在界面中没有 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);
}
}
我们有一些共同的代码。我们想要一个接受 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;
}
}
但是,这会产生编译错误。
Cannot implicitly convert type 'Beta' to 'IExample<ISmallInterface>'. An explicit conversion exists (are you missing a cast?)
将 IExample
更改为使用协变类型参数修复了此转换问题,但破坏了 Where
方法。
public interface IExample<out TBase>
{
IQueryable<TBase> Where(Expression<Func<TBase, bool>> predicate);
}
给出编译器错误
Invalid variance: The type parameter 'TBase' must be invariantly valid on 'IExample<TBase>.Where(Expression<Func<TBase, bool>>)'. 'TBase' is covariant.
在我们的案例中,我们只能使用 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 core 2.2,如果重要的话,我认为是 C#7.3)
解决方案很简单,似乎遵循 "the Linq way."
只需创建一个具有正确签名的扩展方法并在那里实现它。现在界面中没有 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);
}
}