I have Specification pattern implementation and I wanted to change it to support contravariance. However 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 work as you can expect
new Specification<DerivedClass>((x) => true) & new Specification<BaseClass> ((x) => true)
but if I reverse the order of argument it doesn't compile anymore
new Specification<BaseClass>((x) => true) & new Specification<DerivedClass>((x) => true)
I understand why this happens but my question is - is there a way to have both working?
EDIT:
I already tried to define operator & with reverse order or params like that
public static Specification<T> operator &(ISpecification<T> left, Specification<T> right)
{
return new Specification<T>((x) => left.Predicate(x) && right.Predicate(x));
}
but I am getting ambiguous call compiler error between both operators. I am using .NET 4.5
netfiddle: https://dotnetfiddle.net/GB66UN
Yes -- just do it again for the other parameter order.
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 of the enclosing type, only that one of the parameters is.
AS @DStanley points out, even this will fail on a call of 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));
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With