2012-06-27 35 views
12

Vì vậy, một phương pháp khá phổ biến mở rộng cho IEnumerable, Run:Tại sao độ phân giải quá tải của C# không hoạt động giữa Func <T,T> và Hành động <T>?

public static IEnumerable<T> Run<T>(this IEnumerable<T> source, Action<T> action) 
{ 
    foreach (var item in source) 
    { 
     action(item); 
     yield return item; 
    } 
} 

Khi tôi cố gắng sử dụng với, ví dụ, DbSet.Add:

invoice.Items.Run(db.InvoiceItems.Add); 
// NB: Add method signature is 
// public T Add(T item) { ... } 

... trình biên dịch phàn nàn rằng nó có kiểu trả về sai, vì nó đang mong đợi một phương thức void. Vì vậy, thêm một tình trạng quá tải cho Run mà phải mất một Func thay vì hành động:

public static IEnumerable<T> Run<T>(this IEnumerable<T> source, Func<T, T> action) 
{ 
    return source.Select(action).ToList().AsEnumerable(); 
} 

Và bây giờ trình biên dịch phàn nàn rằng "Cuộc gọi là mơ hồ giữa các phương pháp sau ..."

Vì vậy, câu hỏi của tôi là, làm thế nào có thể hành động quá tải của phương pháp Run gây ra sự mơ hồ khi nó không hợp lệ cho nhóm phương pháp?

+0

Chữ ký của 'db.InvoiceItems.Add' là gì? – leppie

+0

T công cộng Thêm (T mục) {...} –

+0

Câu trả lời ngắn: 'x => x.ToString()' lambda này đơn giản nên gọi ToString hoặc gọi ToString và trả về kết quả của nó?Nói cách khác, lambda này có nên được xử lý như một func hay một hành động không? Trình biên dịch không thể đưa ra quyết định này cho bạn do đó, do đó có lỗi. – Polity

Trả lời

5

Điều này đã được giải thích bởi Eric và Jon trong câu trả lời cho this question. Câu chuyện dài ngắn - đây là cách trình biên dịch C# hoạt động; chính xác, khi giao dịch với chuyển đổi nhóm phương pháp quyết định những gì đại biểu nó sẽ được chuyển đổi sang sử dụng độ phân giải quá tải, mà không mất trở lại các loại trong tài khoản:

Nguyên tắc ở đây là rằng xác định nhóm phương pháp chuyển đổi đòi hỏi việc lựa chọn một phương pháp từ nhóm phương pháp sử dụng độ phân giải quá tải và độ phân giải quá tải không xem xét các loại trả về.

Trong biên dịch dụ của bạn thấy cả hai Action<T>Func<T, T> như phù hợp nhất cho Add. Điều này cho biết thêm hai lựa chọn có thể, và vì nó đòi hỏi một lỗi thích hợp được đưa ra.

0

thử tình trạng quá tải ở đúng cách:

public static IEnumerable<TDest> Run<TSource, TDest>(this IEnumerable<TSource> source, 
    Func<TSource, TDest> action) 
{ 
return source.Select(action).ToList(); 
} 
+0

Không tạo ra chút khác biệt nào. –

+0

Bạn nên loại bỏ các .ToList ở đây, để tránh thực hiện các truy vấn –

+0

@SteveB Tên phương pháp là Run, nhưng không LazyRun –

0

tôi không thể trả lời tại sao nhưng để giải quyết sự mơ hồ, bạn có thể đúc một cách rõ ràng chức năng của bạn:

invoice.Items.Run((Func<T,T>)db.InvoiceItems.Add); 

hoặc sử dụng một lambda

invoice.Items.Run(x => db.InvoiceItems.Add(x)); 
0

Tôi không biết tại sao nó không thể tự động giải quyết, nhưng đây là hai cách giải quyết:

// with T replaced with the actual type: 
invoice.Items.Run((Func<T, T>)db.InvoiceItems.Add); 
invoice.Items.Run(new Func<T, T>(db.InvoiceItems.Add)); 

Tại sao bạn vẫn cần những phương pháp này? Có vấn đề gì với:

foreach (var item in invoice.Items) 
    db.InvoiceItems.Add(item); 

Khả năng đọc này tốt hơn nhiều. Trừ khi bạn có lý do chính đáng để cần phương pháp Run, tôi khuyên bạn không nên sử dụng phương pháp này. Từ những gì tôi đã nhìn thấy, không có lý do như vậy, ít nhất là cho quá tải Action<T>.

+0

Chạy là một hoạt động khai báo kiểu chức năng phổ biến, và tôi không đồng ý rằng hình thức foreach dễ đọc hơn. Hơn nữa, một khi bạn đã đặt niềng răng vào, đó là bốn dòng thay vì một. Tôi đang làm điều này cho nửa tá bộ sưu tập trẻ em; thêm vào một dòng trống giữa mỗi dòng và đó là ~ 30 dòng mã, làm cho phương thức chứa quá dài, vì vậy tôi refactor mỗi phương thức foreach ra thành một phương thức riêng biệt. Sau đó, tôi refactor rằng phương pháp để giữ cho mọi thứ DRY và, hey mau, tôi đã có một phương pháp Run anyway. –

+1

@MarkRendle 'Run()' của bạn không phải là rất chức năng. Phần lớn các lập trình hàm là viết các hàm không có tác dụng phụ. Và 'Run()' chỉ hữu dụng * * cho các tác dụng phụ. Tôi đồng ý với Tim về điều này: 'foreach' dễ đọc hơn và sử dụng các phương thức như' Run() 'không phải là một thực hành rất tốt. – svick

+0

@svick Tôi trân trọng không đồng ý. –

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