2016-04-21 39 views
5

Ho implementato il modello di specifica e volevo cambiarlo per supportare la controvarianza. Tuttavia è sorto un problema interessante.Contravarianza e sovraccarico dell'operatore

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

questo lavoro come ci si può aspettare

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

ma se invertire l'ordine di argomenti non più a compilare

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

capisco perché questo accade, ma la mia domanda è - C'è un modo per avere entrambi funzionanti?

EDIT:

Ho già provato a definire operatore & con ordine inverso o params come quello

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

ma sto ottenendo errore del compilatore chiamata ambiguo tra i due operatori. Sto usando .NET 4.5

netfiddle: https://dotnetfiddle.net/GB66UN

risposta

6

Sì - basta farlo di nuovo per l'altro ordine del parametro.

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

sovraccarico operatore non richiede che il parametro primo essere del tipo contenitore, solo uno dei parametri è.

AS @DStanley sottolinea, anche questa avrà esito negativo su una chiamata della forma

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

Così lo facciamo di nuovo, per questa particolare combinazione di parametri:

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

L'ho già provato ma sto ricevendo una chiamata ambigua tra entrambi gli operatori – ekalchev

+0

Interessante, potrebbe essere una mancata corrispondenza della versione. Ho questo lavoro in LINQPad 5, che si rivolge a .NET 4.6 e utilizza C# 6. So che l'implementazione per la risoluzione di sovraccarico ha visto le modifiche. –

+2

LINQPad 4 (.NET 4.5) accetta anche questo. Puoi mostrare il codice completo che dà l'errore - in particolare, la chiamata che fallisce dopo che entrambi i sovraccarichi sono a posto? –

2

Per Supporta sia Covariance & controvarianza che è necessario sovraccaricare l'operatore in questo modo

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