2015-02-24 13 views
24

ReSharper sugeruje mi, aby parametr typu T kontrawariantny zmieniając w ten sposób:Dlaczego ReSharper sugeruje mi wprowadzić parametr przeciwtekstowy typu T?

interface IBusinessValidator<T> where T: IEntity 
{ 
    void Validate(T entity); 
} 

Do tego:

interface IBusinessValidator<in T> where T: IEntity 
{ 
    void Validate(T entity); 
} 

Co więc różni się między <T> i <in T>? I jaki jest cel kontrawariatu tutaj?

Powiedzmy, że mam jednostki IEntity, Entity, User i Account. Zakładając, że zarówno i mają właściwość, którą należy sprawdzić.

Jak mogę zastosować użycie przeciwwskazania w tym przykładzie?

+2

Czy przeczytałeś już dokumentację? https://msdn.microsoft.com/en-us/library/dd799517(v=vs.110).aspx –

+0

Tak, już to przeczytałem. Mam jednak mały problem ze zrozumieniem tego artykułu. W każdym razie link pomógł. –

Odpowiedz

16

So what is different between <T> and <in T>?

Różnica polega na tym, że in T pozwala przechodzić bardziej rodzajowe (mniej pochodzi) typu niż to, co zostało określone.

And what is the purpose of contravariant here?

ReSharper sugeruje użycie kontrawariancji tutaj, ponieważ widzi jesteś przechodzącą parametr T do metodę Validate i chce, aby umożliwić Ci rozszerzyć typ wejścia przez co mniej rodzajowy.

Generalnie kontrawariancja jest tłumaczona na długość w Contravariance explained oraz w Covariance and contravariance real world example i oczywiście w całej dokumentacji na MSDN (jest tam great FAQ by the C# team).

Jest ładny przykład poprzez MSDN:

abstract class Shape 
{ 
    public virtual double Area { get { return 0; }} 
} 

class Circle : Shape 
{ 
    private double r; 
    public Circle(double radius) { r = radius; } 
    public double Radius { get { return r; }} 
    public override double Area { get { return Math.PI * r * r; }} 
} 

class ShapeAreaComparer : System.Collections.Generic.IComparer<Shape> 
{ 
    int IComparer<Shape>.Compare(Shape a, Shape b) 
    { 
     if (a == null) return b == null ? 0 : -1; 
     return b == null ? 1 : a.Area.CompareTo(b.Area); 
    } 
} 

class Program 
{ 
    static void Main() 
    { 
     // You can pass ShapeAreaComparer, which implements IComparer<Shape>, 
     // even though the constructor for SortedSet<Circle> expects 
     // IComparer<Circle>, because type parameter T of IComparer<T> is 
     // contravariant. 
     SortedSet<Circle> circlesByArea = 
      new SortedSet<Circle>(new ShapeAreaComparer()) 
       { new Circle(7.2), new Circle(100), null, new Circle(.01) }; 

     foreach (Circle c in circlesByArea) 
     { 
      Console.WriteLine(c == null ? "null" : "Circle with area " + c.Area); 
     } 
    } 
} 

How can I apply the usage of contravariant in this example?

Powiedzmy mamy nasze podmioty:

public class Entity : IEntity 
{ 
    public string Name { get; set; } 
} 

public class User : Entity 
{ 
    public string Password { get; set; } 
} 

Mamy też IBusinessManager interfejs i implementację BusinessManager, który akceptuje IBusinessValidator:

public interface IBusinessManager<T> 
{ 
    void ManagerStuff(T entityToManage); 
} 

public class BusinessManager<T> : IBusinessManager<T> where T : IEntity 
{ 
    private readonly IBusinessValidator<T> validator; 
    public BusinessManager(IBusinessValidator<T> validator) 
    { 
     this.validator = validator; 
    } 

    public void ManagerStuff(T entityToManage) 
    { 
     // stuff. 
    } 
} 

Teraz, powiedzmy, że stworzyliśmy ogólny walidator dla każdego IEntity:

public class BusinessValidator<T> : IBusinessValidator<T> where T : IEntity 
{ 
    public void Validate(T entity) 
    { 
     if (string.IsNullOrWhiteSpace(entity.Name)) 
      throw new ArgumentNullException(entity.Name); 
    } 
} 

A teraz chcemy przekazać BusinessManager<User> i IBusinessValidator<T>. Ponieważ jest to contravariant, mogę go przekazać BusinessValidator<Entity>.

Jeśli usuniemy słowa kluczowego in, otrzymujemy następujący błąd:

Not contravariant

Jeśli uwzględnimy to, to kompiluje grzywny.

+0

Czy to nie sugeruje, ponieważ jest uznane za nieważne? – Amit

+0

Co nieważne ma wspólnego z kontrawariancją? Sugeruje to, ponieważ przekazuje 'T' do metody. Jeśli używałby go jako typu zwrotu, sugerowałby 'out T' –

+0

Więc w T lub na zewnątrz T nie jest głównym pytaniem, na które OP szuka odpowiedzi? A w twoim przykładzie, jak OP się do tego przyczyni? – Amit

0

Aby zrozumieć motywację ReSharper za rozważyć Marcelo Cantos's donkey gobbler:

// Contravariance 
interface IGobbler<in T> { 
    void gobble(T t); 
} 

// Since a QuadrupedGobbler can gobble any four-footed 
// creature, it is OK to treat it as a donkey gobbler. 
IGobbler<Donkey> dg = new QuadrupedGobbler(); 
dg.gobble(MyDonkey()); 

Jeśli Marcelo zapomniał użyć słowa kluczowego in w deklaracji jego interfejs IGobbler, to układ typu C# 's nie rozpoznać jego QuadrupedGobbler jako gobbler oślego , a więc to zadanie z kodu powyżej zawiedzie skompilować:

IGobbler<Donkey> dg = new QuadrupedGobbler(); 

pamiętać, że nie zatrzyma QuadrupedGobbler z gobb osły ling - na przykład, następujący kod by praca:

IGobbler<Quadruped> qg = new QuadrupedGobbler(); 
qg.gobble(MyDonkey()); 

Jednakże, nie byłby w stanie przypisać QuadrupedGobbler do zmiennej typu IGobbler<Donkey> lub przekazać go do jakiejś metody za IGobbler<Donkey> parametru. Byłoby to dziwne i niekonsekwentne; jeśli QuadrupedGobbler może pożerać osły, to czy nie czyni to z tego rodzaju gobblera osła? Na szczęście, ReSharper zauważy tę niespójność, a jeśli pominąć in w deklaracji IGobbler, to sugerujemy go dodać - z sugestią „Make parametr typu T kontrawariantny” - pozwalając QuadrupedGobbler być stosowany jako IGobbler<Donkey>.

Zasadniczo ta sama logika, o której mowa powyżej, ma zastosowanie w każdym przypadku, gdy deklaracja interfejsu zawiera parametr ogólny, który jest używany tylko jako typ metody: parametry, a nie typy zwracane.

Powiązane problemy