Contravariance and operator overload

I have an implementation of a specification template, and I wanted to change it to support contravariance. However, an interesting problem arose.

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)); } } 

this job as you can expect

 new Specification<DerivedClass>((x) => true) & new Specification<BaseClass> ((x) => true) 

but if I reverse the order of the argument, it no longer compiles

 new Specification<BaseClass>((x) => true) & new Specification<DerivedClass>((x) => true) 

I understand why this happens, but my question is: is there a way to work how?

EDIT:

I already tried to define the operator with the reverse order or parameters like

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

but I get an ambiguous call compiler error between both statements. I am using .NET 4.5

netfiddle: https://dotnetfiddle.net/GB66UN

+5
source share
2 answers

Yes - just do it again for a different order of parameters.

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

Operator overloading does not require that the first parameter be included in the enclosing type, only one of the parameters.

AS @DStanley indicates that even this will not work when calling the form

 new Specification<DerivedClass>((x) => true) & new Specification<DerivedClass>((x) => true); 

So, we do it again, for this particular combination of parameters:

 public static Specification<T> operator &(Specification<T> left, Specification<T> right) { return new Specification<T>((x) => left.Predicate(x) && right.Predicate(x)); } 
+6
source

To support covariance and contravariance, you need to overload the operator as follows:

 public static Specification<T> operator &(ISpecification<T> left, Specification<T> right) { return new Specification<T>((x) => left.Predicate(x) && right.Predicate(x)); } public static Specification<T> operator &(Specification<T> left, ISpecification<T> right) { return new Specification<T>((x) => left.Predicate(x) && right.Predicate(x)); } public static Specification<T> operator &(Specification<T> left, Specification<T> right) { return new Specification<T>((x) => left.Predicate(x) && right.Predicate(x)); } 
+2
source

Source: https://habr.com/ru/post/1247624/


All Articles