2015-02-24 142 views
24

ReSharper的建議我通過改變本作類型參數T逆變:爲什麼ReSharper建議我讓類型參數T逆變?

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

進入這個:

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

那麼,什麼是<T><in T>之間有什麼不同?這裏逆變的目的是什麼?

假設我有IEntity,Entity,UserAccount實體。假設UserAccount都有Name屬性需要驗證。

如何在本例中應用逆變的用法?

+2

您是否已閱讀文檔? https://msdn.microsoft.com/zh-cn/library/dd799517(v=vs.110).aspx – 2015-02-24 03:01:21

+0

是的,我已閱讀它。不過,我對理解文章有點問題。無論如何,鏈接有幫助。 – 2015-02-24 08:16:35

回答

16

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

不同的是,in T允許你傳遞一個更通用的(少派生)類型比指定什麼。

And what is the purpose of contravariant here?

ReSharper的建議在這裏使用逆變,因爲它看到你傳遞的T參數Validate方法,並希望允許您通過使其不太通用拓寬輸入類型。

一般來說,反向變化在Contravariance explainedCovariance and contravariance real world example中解釋爲長度,當然在整個MSDN文檔中(有一個great FAQ by the C# team)。

有通過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?

比方說,我們有我們的實體:

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

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

我們也有一個IBusinessManager接口和BusinessManager實現,它接受一個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. 
    } 
} 

現在,讓我們說,我們創建了一個通用驗證任何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); 
    } 
} 

而現在,我們想通過BusinessManager<User>IBusinessValidator<T>。因爲它是contravariant,我可以通過它BusinessValidator<Entity>

如果我們去掉in關鍵字,我們得到以下錯誤:

Not contravariant

如果包括它,這個編譯罰款。

+0

它是不是暗示,因爲它被宣佈爲無效? – Amit 2015-02-24 04:45:06

+0

虛空與逆變有什麼關係?這是暗示,因爲他正在將'T'傳遞給該方法。如果它使用它作爲返回類型,它會建議'out T' – 2015-02-24 04:59:41

+0

因此在T或out T中OP不是OP尋求答案的主要問題?在你的例子中,OP將如何與之相關? – Amit 2015-02-24 05:04:16

0

要了解ReSharper的動機,考慮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()); 

如果馬塞洛忘了使用in關鍵字在他IGobbler接口的聲明,那麼C#的類型系統將不承認他的QuadrupedGobbler驢子火雞,所以從這個代碼分配上述將無法編譯:

​​

注意,這不會阻止gobb的QuadrupedGobbler靈驢 - 例如,下面的代碼工作:

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

但是,您將無法爲QuadrupedGobbler分配給IGobbler<Donkey>類型的變量或將其傳遞給某個方法的參數IGobbler<Donkey>。這會很奇怪和不一致;如果QuadrupedGobbler可以吞食驢子,那麼這不就是一種驢g??幸運的是,ReSharper注意到這種不一致性,如果您在IGobbler聲明中省略in,則會建議您添加它 - 建議「使類型參數T逆變」 - 允許QuadrupedGobbler用作IGobbler<Donkey>

一般來說,上述同樣的邏輯適用於任何情況下,其中一個接口聲明包含一個通用參數,該參數僅用作方法參數而不是返回類型。

相關問題