2010-01-03 42 views
15

Tôi đã đọc một cuộc phỏng vấn với Joshua Bloch trong Coders at Work, nơi ông than thở việc giới thiệu Generics trong Java 5. Ông không thích việc thực hiện cụ thể phần lớn bởi vì sự hỗ trợ phương sai - các ký tự đại diện của Java — làm cho nó phức tạp một cách không cần thiết.C# generics - không có giới hạn dưới bởi thiết kế?

Theo như tôi biết, C# 3 không có bất kỳ thứ gì như ký tự đại diện rõ ràng, bị chặn, ví dụ: bạn không thể khai báo một phương thức PriceBatch lấy một bộ sưu tập Asset hoặc bất kỳ phân lớp Asset nào (void PriceBatch(Collection<? extends Asset> assets) trong Java?).

Có ai biết tại sao ký tự đại diện và giới hạn chưa được thêm vào C#? Những tính năng này có được bỏ qua một cách có chủ ý để làm cho ngôn ngữ trở nên đơn giản hơn hay đây là thứ mà họ chưa thực hiện để thực hiện?

EDIT: Hút thuốc lá, nhận xét từ Eric Lippert! Sau khi đọc ý kiến ​​sâu sắc của Paul của mình và, tôi nhận ra rằng ít nhất cận trên được hỗ trợ và rằng ví dụ trên có thể được dịch sang C# như:

void PriceBatch<T>(ICollection<T> assets) where T : Asset 

giới hạn thấp hơn, mặt khác, được dường như không được hỗ trợ như Eric nói trong bình luận thứ hai của mình, ví dụ có thể không có cách nào để dịch trực tiếp mã Java này (phần nào được giả tạo) sang C#:

public class Asset {} 
public class Derivative extends Asset {} 
public class VanillaOption extends Derivative {} 

public static <T extends Asset> void copyAssets(Collection<T> src, Collection<? super T> dst) { 
    for(T asset : src) dst.add(asset); 
} 

Collection<VanillaOption> src = new ArrayList<VanillaOption>(); 
[...] 
Collection<Derivative> dst = new ArrayList<Derivative>(); 
[...] 
copyAssets(src, dst); 

Tôi có đúng không? Nếu đây là trường hợp, có một lý do cụ thể tại sao C# có giới hạn trên nhưng không thấp hơn?

+5

Tôi không hiểu câu hỏi; C# đã giới hạn generics và có kể từ C# 2. Là những gì bạn đang nhận được ở đó Java hỗ trợ * call-site * hiệp phương sai và contravariance trên các loại chung được xây dựng? Bởi vì C# không hỗ trợ điều đó; C# 4 sẽ hỗ trợ phương thức * declaration-site *. –

+6

Hoặc là những gì bạn đang nhận được ở thực tế là loại tham số giới hạn trên các phương pháp chung chỉ là bắt nguồn từ giới hạn, và không phải là bắt nguồn từ giới hạn? C# không hỗ trợ cái sau; Tôi tin rằng Java và Scala. –

+0

Cảm ơn, tôi đã cập nhật câu hỏi. – ehnmark

Trả lời

20

Câu hỏi phức tạp.

Trước tiên, hãy xem xét câu hỏi cơ bản của bạn, "tại sao điều này bất hợp pháp trong C#?"

class C<T> where T : Mammal {} // legal 
class D<T> where Giraffe : T {} // illegal 

Đó là, một loại chế generic có thể nói 'T phải bất kỳ loại tài liệu tham khảo có thể được gán cho một biến kiểu động vật có vú', nhưng không phải là" T phải bất kỳ loại tài liệu tham khảo, một biến trong đó có thể được chỉ định một con hươu cao cổ "Tại sao sự khác biệt?

Tôi không biết. Đã lâu trước thời gian của tôi trong nhóm C#. Câu trả lời nhỏ là" vì CLR không hỗ trợ nó ", nhưng nhóm mà thiết kế các generics C# là cùng một đội đã thiết kế các generics CLR, do đó, đó thực sự không phải là một lời giải thích cụ thể.

Dự đoán của tôi sẽ đơn giản như mọi khi, để được hỗ trợ một tính năng phải được thiết kế, triển khai, thử nghiệm, ghi chép và chuyển giao cho khách hàng; không ai từng làm những điều đó cho tính năng này, và do đó nó không có trong ngôn ngữ. Tôi không thấy một lợi ích lớn, hấp dẫn đối với tính năng được đề xuất; các tính năng phức tạp không có lợi ích hấp dẫn có xu hướng được cắt xung quanh đây.

Tuy nhiên, đó là phỏng đoán. Lần sau, tôi tình cờ trò chuyện với những người làm việc về Generics - họ sống ở Anh, nên không phải là họ đang ở dưới hành lang của tôi, thật không may - tôi sẽ hỏi.

Đối với ví dụ cụ thể của bạn, tôi nghĩ rằng Paul là chính xác. Bạn không cần các ràng buộc ràng buộc thấp hơn để thực hiện công việc đó trong C#. Bạn có thể nói:

void Copy<T, U>(Collection<T> src, Collection<U> dst) where T : U 
{ 
    foreach(T item in src) dst.Add(item); 
} 

Đó là, đặt các hạn chế về T, không phải trên U.

+1

Cảm ơn bạn đã theo dõi.Cuốn sách Effective Java giải thích một kịch bản mà điều này sẽ hữu ích: giả sử bạn định nghĩa một kiểu 'MyStack ' với phương thức 'PopAll' lấy một bộ sưu tập đích làm tham số. Nếu bạn có 'MyStack ', bạn sẽ có thể sao chép các mục của nó thành 'Danh sách ', nhưng để thể hiện rằng bạn phải nói điều gì đó giống như khoảng trống công khai 'PopAll (ICollection dst) trong đó T: X' và điều đó dường như là bất hợp pháp. – ehnmark

+7

Đúng, điều đó sẽ hữu ích. Thật thú vị, trong C# mặc dù bạn không thể làm điều đó trực tiếp, bạn * có thể * làm điều đó bằng cách thực hiện một phương pháp mở rộng trên Stack ! Điều đó giới thiệu một tham số kiểu mới mà sau đó có thể bị ràng buộc. –

+0

Một ví dụ khác tôi có thể thấy sẽ là hiệp phương sai của 'IImmutableList ' trong 'System.Collections.Immutable'; hiện tại, 'IImmutableList Add (T item)' ngăn chặn điều đó, nhưng một 'IImmutableList Add (TNew item) trong đó TNew: T' sẽ lưu nó. (Vâng, do đó, sẽ không thực hiện giao diện có thể thay đổi trên loại bất biến để bắt đầu, nhưng tôi cho rằng họ có lý do để đi với thiết kế đó.) Ở đây, các phương pháp mở rộng sẽ không giúp bạn đi xa. – fuglede

8

C# 4 giới thiệu các tính năng mới cho phép hiệp phương sai và contravariance trong generics.

Có SO bài viết khác mà nói chuyện về vấn đề này chi tiết hơn: How is Generic Covariance & Contra-variance Implemented in C# 4.0?

Các tính năng mới không cho phép điều này tự động trong tất cả các loại, nhưng có một cú pháp mới cho phép các nhà phát triển để xác định xem đối số chung là covariant hoặc contravariant.

Phiên bản C# trước C# 4 có chức năng giới hạn tương tự như chức năng này vì nó liên quan đến đại biểu và loại mảng nhất định. Đối với các đại biểu, các đại biểu nhận các loại tham số cơ sở được phép. Đối với các loại mảng, tôi nghĩ nó hợp lệ trừ khi có liên quan đến quyền anh. Nghĩa là, một mảng Khách hàng có thể là trường hợp đối với một mảng đối tượng. Tuy nhiên, một mảng int không thể được đúc thành một mảng các đối tượng.

+0

Mảng cũng vậy: Chuỗi [] cũng là một đối tượng []. – codekaizen

+0

Cập nhật câu trả lời của tôi, cảm ơn. – Eilon

+2

Bộ sưu tập/mảng có thể thay đổi phải không phải là biến thể. Nếu không nó sẽ phá vỡ an toàn loại. –

7

NET đã có tương đương với ký tự đại diện, một cách logic hơn tên là trở ngại kiểu chung chung, bạn có thể làm những gì bạn mô tả không có vấn đề

namespace ConsoleApplication3 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
     List<a> a = new List<a>(); 
     List<b> b = new List<b>(); 
     List<c> c = new List<c>(); 
     test(a); 
     test(b); 
     test(c); 

     } 

     static void test<T>(List<T> a) where T : a 
     { 
      return; 
     } 
    } 
    class a 
    { 

    } 
    class b : a 
    { 

    } 
    class c : b 
    { 

    } 
} 

Ví dụ 2

namespace ConsoleApplication3 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      ICollection<VanillaOption> src = new List<VanillaOption>(); 
     ICollection<Derivative> dst = new List<Derivative>(); 
     copyAssets(src, dst); 
     } 

     public static void copyAssets<T,G>(ICollection<T> src, ICollection<G> dst) where T : G { 
      foreach(T asset in src) 
       dst.Add(asset); 
     } 
    } 
    public class Asset {} 
    public class Derivative : Asset {} 
    public class VanillaOption : Derivative {} 
} 

dụ này thể hiện chuyển đổi mã từ ví dụ của bạn trong java.

Tôi không thực sự ở vị trí để trả lời câu hỏi thực tế!

+0

Hi Paul, một câu hỏi: ngụ ý của ví dụ mã của bạn là ràng buộc chung được thể hiện trong "nơi T: a" ... bởi vì thực tế nó sẽ "xử lý" không chỉ một cá thể "a", mà là , cũng có, bất kỳ đối tượng nào đi xuống, cho dù "gián tiếp", từ "a": tương đương với các ký tự đại diện trong Generics trong Java? Chỉ cần tìm cách làm rõ cho lợi ích của riêng tôi; không có lời chỉ trích nào về câu trả lời của bạn được ngụ ý hoặc dự định. Thực tế là chữ "a" có ba ý nghĩa khác nhau (lớp, biến nội bộ và tên tham số) trong ví dụ của bạn khiến tôi khó khăn hơn trong việc lúng túng, fyi. – BillW

+0

Vâng, có một chút không rõ ràng, xin lỗi. Tôi đã viết một ví dụ về mã giới hạn dưới của bạn, có chức năng nó giống nhau, nhưng nó không sử dụng giới hạn thấp hơn! –

+0

Xin chào Paul và cảm ơn câu trả lời cập nhật của bạn. Câu hỏi của tôi thực sự là tại sao C# 3 chỉ có một tập hợp con hỗ trợ phương sai của Java trong khía cạnh này và cho dù đó là một quyết định có chủ ý để làm "đúng" những gì Java đã làm "sai", hoặc phải làm với các giới hạn CLR hay không. Thật khó để tìm thấy một ví dụ có ý nghĩa khi giới hạn dưới sẽ hữu ích, như ví dụ giả tạo của tôi minh họa, và có lẽ đó là một phần của câu trả lời :) – ehnmark

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