2009-02-12 22 views
7

Tôi đang cố gắng tìm ra một cách để tự động đúc một cái gì đó cho một hành động hoặc Func và tốt nhất mà tôi có thể đưa ra là một cái gì đó như thế này:Tại sao tôi không thể ngầm đúc một Đại biểu bằng các phương pháp Tiện ích mở rộng?

[TestFixture] 
public class ExecutionTest 
{ 
    public void BadMethod() 
    { 
     throw new Exception("Something bad happened"); 
    } 

    [Test] 
    public void TestBadMethod() 
    { 
     // Want this, but it won't work!! 
     // BadMethod.Execute().IgnoreExceptions(); 

     // Ick 
     ((Action)BadMethod).Exec().IgnoreExceptions(); 

     // Still ick 
     ((Action)BadMethod).IgnoreExceptions(); 

     // Do not want 
     ExtensionMethods.Exec(BadMethod).IgnoreExceptions(); 

     // Better but still meh 
     this.Exec(BadMethod).IgnoreExceptions(); 

    } 
} 

public static class ExtensionMethods 
{ 
    public static Action Exec(this Action action) 
    { return action; } 

    public static Action Exec(this object obj, Action action) 
    { return action; } 

    public static void IgnoreExceptions(this Action action) 
    { 
     try { action(); } 
     catch {} 
    } 
} 

Có phải một/cách tốt hơn dễ dàng hơn để làm điều này, bất kỳ suy nghĩ?

Trả lời

5

Trong C#, khi bạn sử dụng tên phương thức không có dấu ngoặc đơn, nó được gọi là nhóm phương thức và nó không có biểu diễn nào khác ngoài thời gian biên dịch. Một nhóm phương thức có thể đại diện cho nhiều hơn một phương thức (vì quá tải và ghi đè), do đó để xác định rõ phương thức nào là cần thiết, một loại đại biểu đích phải được cung cấp.

Trong trường hợp của bạn, bạn đang tự hỏi tại sao loại tham số phương thức tiện ích mở rộng sẽ không kích hoạt độ phân giải của hàm. Nói một cách đơn giản, tiện ích mở rộng được đánh giá sau khi loại được biết, có nghĩa là, tham số này không thể được sử dụng làm mục tiêu chuyển đổi ẩn.

Ví dụ về lý do tại sao nó sẽ phá vỡ:

class Test 
{ 
    void M (void) // Fits Action delegate 
    { 
    } 

    int M (int) // Fits Func<int,int> delegate 
    { 
     return 5; 
    } 

    void Test() 
    { 
     M.Exec(); // UHOH!!! Which Exec to resolve to ??? 
    } 
} 


public static class Extensions 
{ 
    public static void Exec(this Action action) { } 
    public static void Exec(this Func<int, int> func) { } 
} 

Như bạn có thể thấy, có sự mâu thuẫn, nhưng như một vấn đề của thực tế, cuộc xung đột không bao giờ xảy ra vì C# thậm chí sẽ không cố gắng để tìm thấy một phù hợp mở rộng với một nhóm phương pháp.

Lưu ý cách này sẽ không làm việc, hoặc:

class A 
{ 
    public static implicit operator int (A a) 
    { 
     return 5; 
    } 

    void F() 
    { 
     A a = new A(); 
     a.Blah(); // Error! It won't implicitly try C.Blah() 
    } 
} 

public static class C 
{ 
    public static void Blah (int i) 
    { 
    } 
} 

C# sẽ không phù hợp A-C.Blah(int) vì nó sẽ đòi hỏi một chuyển đổi ngầm.

+0

Không chắc chắn rằng đó là một vấn đề của quá tải phương pháp bởi vì trình biên dịch sẽ tìm ra cái nào để gọi.Nhưng đây là câu trả lời tốt nhất mà tôi đã nhìn thấy cho đến nay ... –

2

Như Coincoin nói, nó sẽ không hoạt động tốt trong C# vì tình yêu quá mức cho phương pháp quá tải. Cách giải quyết duy nhất mà tôi đã nhìn thấy người sử dụng là tạo ra hành động và Func phương pháp:

public Action Action(Action f) { return f; } 
public Action<A> Action<A>(Action<A> f) { return f; } 
... 
public Func<A,B,C,D,E> Func(Func<A,B,C,D,E> f) { return f; } 

Bạn thậm chí có thể gọi họ là tất cả "F" để có được một số loại cú pháp ngắn:

F(BadMethod).NoExceptions(); 

Bạn có thể đặt quyết định không định nghĩa các phương thức này trong lớp của bạn, và đặt chúng trong một tiện ích Funcs hoặc một cái gì đó. Alias ​​nó với F và nó không kết thúc quá xấu:

F.F(BadMethod).NoException(); 

Nhưng nhìn chung nó vẫn hút :(

+0

Vâng, đó là lý do tại sao tôi có phương thức mở rộng cho (đối tượng Object này, Action action) vì vậy nó sẽ làm việc bên trong bất kỳ lớp nào bằng cách gọi this.Action() hoặc this.Func() –

+0

Xin lỗi, tôi không thấy t ông Exec định nghĩa - phần nào gây hiểu lầm tên, không: P – MichaelGG

+0

Vâng, tôi đổi tên nó trong mã thực tế của tôi. Nó dường như "thông thạo" vào thời điểm đó. –

1

F # cho phép bạn làm điều này loại điều rất tự nhiên bằng cách cung cấp một loại suy luận tốt hơn nhiều.

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