2015-02-24 16 views
24

ReSharper gợi ý cho tôi để làm cho T contravariant loại tham số bằng cách thay đổi này:Tại sao ReSharper lại đề xuất tôi tạo tham số kiểu T contravariant?

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

Into này:

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

Vì vậy, những gì là khác nhau giữa <T><in T>? Và mục đích của contravariant ở đây là gì?

Giả sử tôi có IEntity, Entity, UserAccount pháp nhân. Giả sử rằng cả hai số UserAccount đều có Name thuộc tính cần được xác thực.

Làm cách nào để áp dụng việc sử dụng biến thể trong ví dụ này?

+2

Bạn đã đọc tài liệu chưa? https://msdn.microsoft.com/en-us/library/dd799517(v=vs.110).aspx –

+0

Có, tôi đã đọc nó. Tuy nhiên tôi có một vấn đề nhỏ với sự hiểu biết bài báo. Dù sao, liên kết đã giúp. –

Trả lời

16

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

Sự khác biệt là in T cho phép bạn chuyển loại chung hơn (ít bắt nguồn) hơn loại được chỉ định.

And what is the purpose of contravariant here?

ReSharper gợi ý để sử dụng contravariance ở đây vì nó nhìn thấy bạn đang đi qua các tham số Tvào phương pháp Validate và muốn để cho phép bạn mở rộng các loại đầu vào bằng cách làm cho nó ít generic.

Nói chung, nghịch lý được giải thích theo chiều dài trong Contravariance explained và trong Covariance and contravariance real world example và tất nhiên trong suốt tài liệu trên MSDN (có great FAQ by the C# team).

Có một ví dụ tốt đẹp thông qua 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?

Hãy nói rằng chúng tôi có tổ chức của chúng tôi:

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

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

Chúng tôi cũng có một giao diện IBusinessManager và thực hiện BusinessManager, mà chấp nhận một 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. 
    } 
} 

Bây giờ, cho phép nói rằng chúng ta đã tạo ra một validator chung cho bất kỳ 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); 
    } 
} 

Và bây giờ, chúng tôi muốn vượt qua BusinessManager<User>IBusinessValidator<T>. Bởi vì nó là contravariant, tôi có thể vượt qua nó BusinessValidator<Entity>.

Nếu chúng ta loại bỏ các từ khóa in, chúng tôi nhận được lỗi sau:

Not contravariant

Nếu chúng tôi bao gồm nó, điều này biên dịch tốt.

+0

Nó không được đề xuất bởi vì nó được khai báo là vô hiệu? – Amit

+0

void phải làm gì với contravariance? Nó cho thấy nó bởi vì anh ta đang truyền 'T' vào phương thức. Nếu nó được sử dụng như là một kiểu trả về, nó sẽ được đề xuất 'out T' –

+0

Vì vậy, trong T hoặc T ra không phải là câu hỏi chính mà OP tìm kiếm câu trả lời? Và trong ví dụ của bạn như thế nào OP sẽ thực hiện cho nó? – Amit

0

Để hiểu động lực ReSharper, hãy xem xét 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()); 

Nếu Marcelo đã quên sử dụng từ khoá in trong việc kê khai của giao diện IGobbler của mình, sau đó loại hệ thống C# 's sẽ không nhận ra QuadrupedGobbler của mình như là một người ăn tạp lừa và do đó, nhiệm vụ này từ mã bên trên sẽ không biên dịch được:

IGobbler<Donkey> dg = new QuadrupedGobbler(); 

Lưu ý rằng điều này sẽ không dừng QuadrupedGobbler từ gobb ling lừa - ví dụ, đoạn mã sau sẽ công việc:

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

Tuy nhiên, bạn sẽ không thể gán một QuadrupedGobbler đến một biến kiểu IGobbler<Donkey> hoặc vượt qua nó để tham số IGobbler<Donkey> của một số phương pháp. Điều này sẽ là lạ và không phù hợp; nếu QuadrupedGobbler có thể nuốt những con lừa, thì điều đó không làm cho nó thành một loại lừa đảo lừa đảo? May mắn thay, ReSharper nhận thấy sự không nhất quán này, và nếu bạn bỏ in trong khai báo IGobbler, nó sẽ gợi ý bạn thêm nó - với đề xuất "Đặt tham số kiểu T contravariant" - cho phép QuadrupedGobbler được sử dụng làm IGobbler<Donkey>.

Nói chung, cùng một logic được nêu ở trên áp dụng trong bất kỳ trường hợp nào khai báo giao diện chứa tham số chung chỉ được sử dụng làm loại phương thức thông số, không trả về loại.

Các vấn đề liên quan