2016-04-21 11 views
5

Ich habe Spezifikation Muster Implementierung und ich wollte es ändern, um Kontravarianz zu unterstützen. Es entstand jedoch ein interessantes Problem.Kontravarianz und Operator Überlast

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

diese Arbeit, wie Sie

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

erwarten würde, aber wenn ich die Reihenfolge Argument umkehren nicht kompiliert es nicht mehr

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

Ich verstehe, warum dies geschieht, aber meine Frage ist - Gibt es eine Möglichkeit, beides zu haben?

EDIT:

Ich habe bereits versucht Operator zu definieren & mit umgekehrter Reihenfolge oder params wie die

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

aber ich bin immer mehrdeutig Aufruf Compilerfehler zwischen den beiden Betreibern. Ich verwende .NET 4.5

netfiddle: https://dotnetfiddle.net/GB66UN

Antwort

6

Ja - nur es wieder tun für die anderen Parameter um.

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

Betreiber Überlastung erfordert nicht, dass die ersten Parameter des einschließenden Typ sein, nur dass ein der Parameter ist.

AS @DStanley darauf hinweist, auch dies wird

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

So auf einen Aufruf der Form nicht wir es wieder tun, für diese besondere Kombination von Parametern:

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

Ich habe das schon versucht, aber ich bekomme mehrdeutigen Anruf zwischen beiden Operatoren – ekalchev

+0

Interessant, das kann eine Versionskonflikt sein. Ich habe dies in LINQPad 5 funktioniert, die .NET 4.6 zielt und C# 6 verwendet. Ich kenne die Implementierung für Überladungsauflösung sah Änderungen. –

+2

LINQPad 4 (.NET 4.5) akzeptiert dies auch. Können Sie den vollständigen Code anzeigen, der den Fehler verursacht - insbesondere den Aufruf, der nach dem Auftreten beider Überladungen fehlschlägt? –

2

zu unterstützen beide Covariance & Kontravarianz Sie müssen den Operator wie folgt überladen

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