逆变和操作员过载

eka*_*hev 5 c# generics c#-4.0

我有规范模式实现,我想改变它以支持逆变.然而,出现了有趣的问题.

public interface ISpecification<in T>
{
    Func<T, bool> Predicate { get; }
    bool IsSatisfiedBy(T entity);
}

public class Specification<T> : ISpecification<T>
{
    public Specification(Func<T, bool> predicate)
    {
        this.Predicate = predicate;
    }

    public Func<T, bool> Predicate
    {
        get;
        private set;
    }

    public bool IsSatisfiedBy(T x)
    {
        return Predicate.Invoke(x);
    }

    public static Specification<T> operator &(Specification<T> left, ISpecification<T> right)
    {
        return new Specification<T>((x) => left.Predicate(x) && right.Predicate(x));
    }
}
Run Code Online (Sandbox Code Playgroud)

你可以期待这项工作

new Specification<DerivedClass>((x) => true) & new Specification<BaseClass> ((x) => true)
Run Code Online (Sandbox Code Playgroud)

但如果我颠倒了参数的顺序,它就不再编译了

new Specification<BaseClass>((x) => true) & new Specification<DerivedClass>((x) => true)
Run Code Online (Sandbox Code Playgroud)

我理解为什么会发生这种情况,但我的问题是 - 有没有办法兼顾两者?

编辑:

我已经尝试使用相反的顺序或params来定义运算符

public static Specification<T> operator &(ISpecification<T> left, Specification<T> right)
{
    return new Specification<T>((x) => left.Predicate(x) && right.Predicate(x));
}
Run Code Online (Sandbox Code Playgroud)

但我在两个运营商之间调用编译器错误模糊不清.我使用的是.NET 4.5

netfiddle:https://dotnetfiddle.net/GB66UN

Jer*_*ert 6

是的 - 再次为其他参数顺序执行此操作.

public static Specification<T> operator &(ISpecification<T> left, Specification<T> right)
{
    return new Specification<T>((x) => left.Predicate(x) && right.Predicate(x));
}
Run Code Online (Sandbox Code Playgroud)

运算符重载不要求第一个参数是封闭类型,只需要其中一个参数.

正如@DStanley指出的那样,即使这会在表单调用时失败

new Specification<DerivedClass>((x) => true) & new Specification<DerivedClass>((x) => true);
Run Code Online (Sandbox Code Playgroud)

所以我们再次这样做,对于这个特定的参数组合:

public static Specification<T> operator &(Specification<T> left, Specification<T> right)
{
    return new Specification<T>((x) => left.Predicate(x) && right.Predicate(x));
}
Run Code Online (Sandbox Code Playgroud)

  • LINQPad 4(.NET 4.5)也接受了这一点.你能显示出错误的完整代码吗 - 特别是在两个重载都到位后失败的调用? (2认同)
  • 如果_both_参数使用派生类作为泛型参数,则不明确.添加_third_重载解决了:) (2认同)
  • @ekalchev:添加第三个重载,你会看到这个场景也有效.https://dotnetfiddle.net/NwootM (2认同)