2010-10-19 71 views
8

Tôi muốn viết một phương pháp mở rộng sẽ hoạt động trên các từ điển có giá trị là một số loại chuỗi. Thật không may, trình biên dịch dường như không thể suy ra các đối số chung từ việc sử dụng phương thức của tôi; Tôi cần phải xác định rõ ràng.Generics: Tại sao trình biên dịch không thể suy ra các đối số kiểu trong trường hợp này?

public static void SomeMethod<TKey, TUnderlyingValue, TValue> 
    (this IDictionary<TKey, TValue> dict) 
    where TValue : IEnumerable<TUnderlyingValue> { }  

static void Usage() 
{ 
    var dict = new Dictionary<int, string[]>(); 
    var dict2 = new Dictionary<int, IEnumerable<string>>(); 

    //These don't compile 
    dict.SomeMethod(); 
    SomeMethod(dict); // doesn't have anything to do with extension-methods 
    dict2.SomeMethod(); // hoped this would be easier to infer but no joy 


    //These work fine 
    dict.SomeMethod<int, string, string[]>(); 
    dict2.SomeMethod<int, string, IEnumerable<string>>(); 
} 

Tôi nhận ra rằng suy luận kiểu không phải là khoa học chính xác, nhưng tôi tự hỏi liệu có một số 'quy tắc' cơ bản tôi thiếu ở đây - tôi không quen với các chi tiết của thông số.

  1. Đây có phải là một thiếu sót của quá trình suy luận hay là kỳ vọng rằng trình biên dịch sẽ "tìm ra" không hợp lý trong trường hợp này (không rõ ràng)?
  2. Tôi có thể thay đổi chữ ký của phương thức theo cách có thể làm cho nó hoạt động bình thường như 'không thể sửa'?
+0

Suy luận TUnderlyingValue có thể khó khăn. Đặc biệt là vì tham số kiểu của 'IEnumerable <>' là sự biến đổi trong .net 4. – CodesInChaos

+5

Suy luận kiểu là một khoa học chính xác có các thuật toán chính thức, nó không chỉ là một phỏng đoán. Nếu một trình biên dịch không thể suy ra một kiểu, nó chỉ vì nó không có các quy tắc cụ thể để xử lý một số trường hợp, và nó thường là một thiết kế thực hiện, không phải là một giới hạn. Hãy xem tại đây: http://www.classes.cs.uchicago.edu/archive/2005/winter/33600-1/slides/Lesson10.pdf – Jack

+0

@Jack: Cảm ơn bạn đã liên kết; Nó thật thú vị. Xem bình luận của tôi về câu trả lời của Eric Lippert cho những gì tôi muốn nói. Nghèo lựa chọn các từ về phần tôi, tôi giả sử. – Ani

Trả lời

15

Tôi nhận ra rằng suy luận kiểu không phải là một khoa học chính xác

Tôi không chắc chắn Tôi đồng ý. Thông số kỹ thuật khá chi tiết.

Tôi đã tự hỏi nếu có một số cơ bản 'quy tắc' Tôi đang mất tích ở đây

Nguyên tắc cơ bản mà bạn đang thiếu là lẽ rằng những hạn chế không phải là một phần của chữ ký. Loại suy luận hoạt động tắt chữ ký.

Có nhiều lý do chính đáng cho quyết định thiết kế của tôi. Tuy nhiên, nhiều người tin rằng tôi sai về mặt đạo đức vì tin rằng có những lý do chính đáng cho quyết định thiết kế đó. Nếu bạn quan tâm đến việc đọc những gì cảm thấy như vài triệu từ về chủ đề liệu tôi có đúng hay sai, hãy xem bài viết của tôi về chủ đề và hàng trăm ý kiến ​​nói với tôi rằng tôi sai:

http://blogs.msdn.com/b/ericlippert/archive/2009/12/10/constraints-are-not-part-of-the-signature.aspx

Đây có phải là thiếu sót của quy trình suy luận không?

Có thể cho là có. Theo tôi, đó là một sự lựa chọn hợp lý cho các yêu cầu thiết kế cạnh tranh. (Đó là "làm những gì người dùng có ý nghĩa" và "đưa ra lỗi khi mọi thứ trông mơ hồ".)

là kỳ vọng rằng trình biên dịch nên "tìm ra" không hợp lý trong trường hợp này?

Không. Bạn có vẻ như một người hợp lý và kỳ vọng của bạn dường như dựa trên lý do chính đáng. Tuy nhiên, hoàn toàn có thể có một kỳ vọng hợp lý mà vẫn chưa được đáp ứng. Đây sẽ là một trong những trường hợp đó.

Tôi có thể thay đổi chữ ký của phương thức theo cách làm cho nó hoạt động bình thường như 'không thể sửa'?

Điều đó sẽ rất khó, vì loại từ điển chung chung không phải là biến thể hoặc contravariant trong chuyển đổi của nó. Khái niệm bạn muốn chụp không dễ dàng được thể hiện trong hệ thống kiểu theo cách suy luận.

Nếu bạn thích sử dụng ngôn ngữ có suy luận kiểu nâng cao hơn, hãy cân nhắc sử dụng F #. Nếu bạn thích ngôn ngữ nghiêng về "làm những gì người dùng có nghĩa là" thay vì "báo cáo lỗi về sự mơ hồ", hãy xem xét sử dụng VB.

+0

Rực rỡ, cảm ơn - "những ràng buộc không phải là một phần của chữ ký" là những gì tôi đã mất tích, và tôi phải quấn quanh đầu. Trên một lưu ý khác, ý tôi là "không phải là một khoa học chính xác" là có thể có hai thuật toán suy luận khác nhau nhưng không phù hợp với spec đó để suy ra các kiểu đối số khác nhau, nhưng cả hai đều được biện minh trong lựa chọn. Bạn có đồng ý không? – Ani

+0

@Ani: Tuyệt đối, có rất nhiều loại inferencers khác nhau có thể. Chúng tôi đã thực hiện một sự lựa chọn có chủ ý để không sử dụng suy luận kiểu kiểu Hindley-Milner trong C#, ví dụ. –

+0

Tên suy luận kiểu mà bạn sử dụng là gì? Chỉ cần tự hỏi nếu nó được phát minh bởi một người khác giống như suy luận kiểu kiểu Hindley-Milner (tôi giả định). –

1

Tại sao không loại bỏ loại IEnumerable?

public static void SomeMethod<TKey, TValue> 
(this IDictionary<TKey, TValue> dict) 
where TValue : IEnumerable { }  
+2

Bởi vì sau đó bạn sẽ không (thiếu phản xạ) có quyền truy cập vào loại giá trị cơ bản - tất cả những gì bạn biết là mỗi 'Giá trị' là một chuỗi * cái gì đó *, nhưng không phải cái gì đó là gì. – AakashM

+0

Bạn có thể truyền TValue tới IEnumerable vì bạn biết đó là loại. –

4

C# kiểu suy luận không hoạt động do các ràng buộc hoặc giá trị trả lại. Vì vậy, bạn sẽ có may mắn hơi tốt hơn với

public static void SomeMethod<TKey, TUnderlyingValue> 
    (this IDictionary<TKey, IEnumerable<TUnderlyingValue>> dict) 
    { } 

Điều này sẽ có tác dụng nếu bạn khai báo các param như new Dictionary< string, IEnumerable<int>>(), nhưng không nếu bạn khai báo nó new Dictionary<string, List<int>>().

Tôi có thể nói, rằng con đường tôi đọc phần 7.5.2 của c# spec, có vẻ như rằng kể từ khi thực hiện List<int>IEnumerable<int>, những suy luận kiểu của TUnderlyingValue nên làm việc. Tuy nhiên, phần đó không thực sự dễ hiểu. Tôi giả sử nó không hoạt động thông qua nhiều "lớp", vì SomeMethod<T>(IEnumberable<T> val){} sẽ hoạt động tốt khi gọi nó với SomeMethod(new List<string>()). Tôi không thấy cụ thể bất cứ điều gì trong spec đó đề cập đến việc giải quyết một loại ở đâu đó U = Ca<Va, Cb<Vb>>, vì vậy có lẽ suy luận ở cấp đó không được xác định.

+0

+1 @Philip Rieck: Cảm ơn. Tôi biết về cách giải quyết này, nhưng nó không giải quyết được trường hợp chung, đó là điều tôi quan tâm. – Ani

+0

@Ani xem các chỉnh sửa - Tôi không chắc rằng suy luận kiểu được xác định và được triển khai trong C# 4.0 hỗ trợ mức độ suy luận chút nào. –

+2

Điều này sẽ không hoạt động với Từ điển > vì IDictionary là một kiểu bất biến. Giả sử thân phương thức chứa đựng điều này: 'dict.Add (new ObservableCollection ());'. Nếu có một giao diện IReadOnlyDictionary của một số loại, nó có thể được thực hiện covariant trên tham số giá trị và cho phép cả hai từ điển để làm việc. –

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