2012-07-20 4 views
9

Tôi không nghĩ rằng tôi đang làm bất cứ điều gì quá bí truyền, nhưng tôi không thấy bất kỳ câu hỏi nào khác về điều này.C# loại đối số không thể được suy ra từ việc sử dụng trong Chọn với nhiều lợi nhuận

Đoạn mã sau (tôi đã giảm nó thành các yếu tố cần thiết) tạo ra lỗi trình biên dịch trong C# 4. Tuy nhiên, phải rõ ràng đối số kiểu là gì - có một mẫu số chung lớn nhất ("lớp A") cũng được định nghĩa rõ ràng trong kiểu trả về của phương thức "Frob". Không nên trình biên dịch tạo danh sách tất cả các kiểu trả về trong biểu thức lambda, tạo một cây tổ tiên để tìm tổ tiên chung của chúng, và sau đó điều chỉnh lại với kiểu trả về dự kiến ​​của phương thức chứa?

Đối số kiểu cho phương thức 'System.Linq.Enumerable.Select (System.Collections.Generic.IEnumerable, System.Func)' không thể suy ra được khi sử dụng. Hãy thử xác định các đối số kiểu một cách rõ ràng.

using System.Collections.Generic; 
using System.Linq; 
using System.Text.RegularExpressions; 

namespace Sample 
{ 
    public abstract class A 
    { 
     private A(int index) { /* ... */ } 

     public sealed class A1 : A 
     { 
      public A1(string text, int index) 
       : base(index) 
      { /* ... */ } 
     } 

     public sealed class A2 : A 
     { 
      public A2(int index) 
       : base(index) 
      { /* ... */ } 
     } 

     private static Regex _regex = new Regex(@"(to be)|(not to be)"); 
     public static IEnumerable<A> Frob(string frobbable) 
     { 
      return _regex.Matches(frobbable) 
       .Cast<Match>() 
       .Select((match, i) => 
       { 
        if (match.Groups[1].Success) 
        { 
         return new A1(match.Groups[1].Value, i); 
        } 
        else 
        { 
         return new A2(i); 
        } 
       }); 
     } 
    } 
} 
+2

Bạn đã thử đúc A1 và A2 thành A trước khi quay lại chưa? Nhưng yeah, tôi có thể thấy lý do tại sao trình biên dịch phàn nàn, nó không kỳ diệu biết bạn muốn mẫu số chung là lớp A. –

Trả lời

16

Đó là phần 7.5.2.12 của C# 4 spec:

Kiểu trả về suy ra của một chức năng ẩn danh F được sử dụng trong quá trình suy luận kiểu và độ phân giải quá tải. Kiểu trả về được suy ra chỉ có thể được xác định cho một hàm ẩn danh trong đó tất cả các kiểu tham số được biết, hoặc vì chúng được cung cấp một cách rõ ràng, được cung cấp thông qua một chuyển đổi hàm ẩn danh hoặc suy luận trong suy luận kiểu trên một lời gọi phương thức chung kèm theo. Loại trả về phỏng đoán được xác định như sau:

  • Nếu nội dung của F là biểu thức, thì kiểu trả về phỏng đoán F là loại biểu thức đó.
  • Nếu nội dung của F là một khối và tập hợp các biểu thức trong câu lệnh trả về của khối có loại T phổ biến nhất (§7.5.2.14), thì kiểu trả về được suy ra của F là T.
  • Nếu không, không thể suy ra loại trả lại cho E.

Mục 7.5.2.14 là:

Trong một số trường hợp, loại phổ biến cần được suy ra cho một tập hợp các biểu thức. Đặc biệt, các kiểu phần tử của các mảng được nhập ngầm và các kiểu trả về của các hàm ẩn danh với các khối khối được tìm thấy theo cách này.

trực giác, được đưa ra một tập hợp các biểu thức E1 ... Em suy luận này phải tương đương với gọi một phương thức

Tr M<X>(X x1 … X xm) 

với Ei như các đối số. Đầu tiên, X được cố định và, nếu thành công, kiểu kết quả S là kiểu kết quả phổ biến nhất, kết quả là kết quả được đưa ra từ mỗi Ei đến X. Cuối cùng, X là cố định và, nếu thành công. cho các biểu thức. Nếu không có S như vậy, các biểu thức không có loại phổ biến nhất.

Vì vậy, giả sử chúng ta có:

void M<X>(X x1, Xx2) {} 

A1 a1 = new A1(); 
A2 a2 = new A2(); 
M(a1, a2); 

... đó sẽ thất bại trong việc xác định một đối số kiểu cho X, vì vậy giá trị trả về kết luận thất bại trong cùng một cách.

Tôi nghi ngờ rằng nếu bạn bỏ hoặc giá trị trả về của bạn là A, nó sẽ hoạt động.

+0

Ahh có thông số C#. Cảm ơn bạn đã đăng bài Jon. –

+1

Rất đúng, truyền một hoặc "A" sẽ hoạt động. –

+0

Vì vậy, về cơ bản spec là nói rằng quá trình suy luận là giống hệt nhau trong cả hai trường hợp. Tôi có thể thấy rằng. Nhưng nó không thực sự giải thích quá trình "sửa chữa X" là gì - đó là vấn đề thực sự, trình biên dịch không thể đưa ra quyết định. Tại sao nó không thể chỉ chọn cụ thể nhất của các loại có thể, nếu nó biết rằng sẽ không có bất kỳ tác dụng phụ từ lựa chọn một cách ("Object)" hoặc khác ("A)? –

6

Tôi đoán có một cụ C# đặc điểm kỹ thuật quy định tại khoản đâu đó rằng mệnh lệnh này. (EDIT: Jon Skeet tìm thấy nó và đăng chúng trong câu trả lời của mình)

Thông thường, các lambdas (hoặc hoạt động ternary, vv) yêu cầu phải có cùng kiểu trả về chính xác ở từng giai đoạn để tránh sự mơ hồ. Ví dụ: trong trường hợp của bạn, bạn có muốn trả lại loại A hoặc Object không? Thậm chí thú vị hơn khi bạn ném các giao diện hoặc nhiều cấp độ thừa kế vào hỗn hợp.

Đặt cược tốt nhất trong trường hợp này là chỉ cần cast mỗi báo cáo lợi nhuận của bạn phải gõ A hoặc lưu trữ nó trong một biến tạm thời:

if (match.Groups[1].Success) 
    return (A)(new A1(match.Groups[1].Value, i)); 
else 
    return (A)(new A2(i)); 

hoặc

A returnValue; 

if (match.Groups[1].Success) 
    returnValue = new A1(match.Groups[1].Value, i); 
else 
    returnValue = new A2(i); 

return returnValue; 

EDIT: Nếu bạn là ok mà không có loại được phỏng đoán, bạn có thể gọi một cách rõ ràng truy vấn Select với:

.Cast<Match>() 
.Select<Match, A>((match, i) => 
{ 
    if (match.Groups[1].Success) 
     return new A1(match.Groups[1].Value, i); 
    else 
     return new A2(i); 
}); 

Sau đó trình biên dịch sẽ chỉ cần đảm bảo loại trở lại của bạn là ngầm tương thích với A (mà họ đang có)

+0

Không; các kiểu trả về không cần phải là indentical. – SLaks

+1

@SLaks Tôi nghĩ về suy luận kiểu họ làm. Nếu Lars đã gõ một cách rõ ràng 'Chọn ', thì chúng có thể là bất kỳ đối tượng nào có thể được xem xét ngầm định dưới dạng kiểu 'A' mà không đúc chúng một cách rõ ràng. –

+0

Cảm ơn bài đăng hữu ích - nhưng đó chỉ là giải pháp thay thế, nó không phải là câu trả lời cho "tại sao" tôi phải làm những gì có vẻ như công việc không cần thiết. Tôi quan tâm nhiều hơn đến khía cạnh "tại sao không thể biên dịch được thông minh theo cách này". –