2011-11-29 28 views
25

Resharper đã gợi ý để thay đổi từ<TFrom, out TTo> nghĩa là gì?

interface IModelMapper<TFrom, TTo> 
{ 
    TTo Map(TFrom input); 
} 

vào

interface IModelMapper<in TFrom, out TTo> 

Vì vậy, tôi điều tra một chút và kết thúc đọc this article (tìm thấy qua một Wikipedia bài viết) và một số chi tiết của Google.

Tôi vẫn không chắc chắn điều này sẽ ngụ ý cho đơn đăng ký của mình vì vậy tôi bị cám dỗ vì không chấp nhận đề xuất. Những lợi ích mà sự thay đổi này sẽ giới thiệu là gì và tôi không cân nhắc việc bỏ qua gợi ý đó?

Rõ ràng hơn, tại sao tôi nên chấp nhận?

Trả lời

48

Bottom Line: Resharper đã điều tra kiểu của bạn, và phát hiện ra rằng TFrom có thể được sử dụng contravariantly, và TTo covariantly. Việc chấp nhận trình tái cấu trúc sẽ cho phép bạn sử dụng các loại này với tính linh hoạt cao hơn, như được mô tả bên dưới. Nếu điều đó có thể có giá trị đối với bạn, hãy chấp nhận nó.

Lưu ý, tuy nhiên, việc chấp nhận trình tinh chỉnh này sẽ các hạn chế về cách bạn sử dụng các loại này trong tương lai. Nếu bạn đã từng viết một phương thức có tham số TTo, bạn sẽ gặp phải lỗi trình biên dịch vì các loại biến thể không thể đọc được. Và ditto cho TFrom: bạn sẽ không bao giờ có phương thức trả về kiểu này hoặc có thông số out thuộc loại này.


Đó là nói với bạn rằng TFrom là contravariant, và TTo đó là hiệp biến. Đây là những tính năng thời gian gần đây added to C#

Loại hiệp phương sai có nghĩa là một hơn loại cụ thể có thể được thông qua tại, trong khi contravariance có nghĩa là một ít loại cụ thể có thể được thông qua trong.

IEnumerable<T> là một ví dụ tốt về loại hiệp phương sai. Kể từ khi các mục trong một IEnumerable<T> được chỉ đọc, bạn có thể đặt nó vào một cái gì đó cụ thể hơn:

IEnumerable<object> objects = new List<string>(); 

Hãy xem xét những gì có thể xảy ra nếu (theo giả thiết), bạn được phép làm việc này cho các bộ sưu tập đã được đọc/ghi :

List<object> objects = new List<string>(); 
objects.Add(new Car()); 
//runtime exception 

Để có kiểu hiệp biến, một tham số chung phải được sử dụng trong một chỉ đọc cách nghiêm ; nó chỉ được viết ra khỏi từ loại này và không bao giờ đọc trong (do đó từ khóa). Đó là lý do tại sao ví dụ IEnumerable<T> hoạt động, nhưng ví dụ List<T> thì không. Nhân tiện, mảng do hỗ trợ phương sai hiệp phương sai (kể từ khi Java thực hiện, tôi tin), và vì vậy cùng loại lỗi thời gian chạy này có thể xảy ra với mảng.

Loại contravariance có nghĩa là ngược lại.Để hỗ trợ loại đối nghịch, thông số chung phải được đọc chỉ trong và không bao giờ viết ra. Điều này cho phép bạn thay thế các loại ít cụ thể hơn trong

Action<T> là một ví dụ về loại contravaince:.

Action<object> objAction = (o => Console.WriteLine(o.ToString())); 
Action<string> strAction = objAction; 
strAction("Hello"); 

strAction được khai báo để có một tham số chuỗi, nhưng nó hoạt động tốt nếu bạn thay thế một loại đối tượng. Một chuỗi sẽ được truyền vào, nhưng nếu đại biểu nó được thiết lập để làm việc với chọn để coi nó như là một đối tượng, thì cũng vậy. Không có hại gì.

Để hoàn thành, Func<T> là trường hợp ngược của Action<T>; đây T chỉ trở lại, do đó nó là hiệp biến:

Func<string> strDelegate = () => "Hello"; 
Func<object> myObjFunc = strDelegate; 
object O = myObjFunc(); 

myObjectFunc được mã hoá để trả về một đối tượng. Nếu bạn đặt nó thành một cái gì đó trả về một chuỗi, sau đó, một lần nữa, không có hại gì được thực hiện.

+0

cảm ơn bạn, nó thực sự hữu ích. – mhttk

+0

@mhttk - tuyệt vời. Rất vui được giúp đỡ –

+0

Có lẽ lời giải thích tốt nhất về đồng/contravariance tôi đã đọc - cảm ơn! –

2

Ví dụ về cách lựa chọn này có thể ảnh hưởng đến ứng dụng của bạn, giả sử bạn có loại CustomerAddressMapper triển khai IModelMapper<Customer, Address[]> và một số khác SupplierAddressMapper triển khai IModelMapper<Supplier, Address[]>. Các loại CustomerSupplier chia sẻ một lớp cơ sở Company, nhưng logic địa chỉ của chúng là khác biệt, vì vậy chúng tôi cần các loại riêng biệt để xử lý điều này.

Bây giờ giả sử rằng bạn có phương thức lấy IMapper<Company, Address[]>. Trước giao diện contravariance, bạn sẽ không thể chuyển các phiên bản CustomerAddressMapper hoặc SupplierAddressMapper vào phương thức này. Bây giờ, nếu bạn sử dụng công cụ sửa đổi in trên thông số loại TFrom, bạn có thể.

Ngoài ra, do hiệp phương sai trên tham số TTo, bạn cũng có thể chuyển một số CustomerAddressMapper đến các phương thức yêu cầu IMapper<Customer, object>.

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