2010-01-20 34 views
10

Tôi là một nhà phát triển C++ có tín hiệu đã sử dụng & vị trí trong c + + mà đối với tôi dường như giống với các đại biểu trong C#. Tôi đã tìm thấy bản thân mình ở một mất mát trong việc tìm kiếm các chức năng được cung cấp bởi "ràng buộc", và cảm thấy tôi phải mất một cái gì đó.(Làm thế nào) là nó có thể ràng buộc/rebind một phương pháp để làm việc với một đại biểu của một chữ ký khác nhau?

Tôi cảm thấy giống như sau, có thể trong C++ nên có thể trong C# với các đại biểu. Dưới đây là một số mã psudo cho những gì tôi sẽ làm trong c + +:

Slot<void> someCallback; 

int foo(int i) 
{ 
    std::cout << "Value: " << i << "\n"; 
    return i; 
} 

int main() 
{ 
    int i = 0; 
    Slot<int> someCallback = bind(fun_ptr(foo), i); 
    ++i; // added to show that late evaluation would be a non-trivial difference 
    int result = someCallback(); 
    assert(result == 0); 
    return 0; 
} 

Thật không may, tôi đã không thể tìm thấy bất kỳ tham chiếu đến ràng buộc/rebinding liên quan đến C# đại biểu. Tui bỏ lỡ điều gì vậy? Có một số cách hoàn toàn khác nhau để làm điều này trong C#?

Trả lời

13

Trong C# chúng ta làm một cái gì đó như thế này:

class Program { 
    static Action Curry<T>(Action<T> action, T parameter) { 
     return() => action(parameter); 
    } 

    static void Foo(int i) { 
     Console.WriteLine("Value: {0}", i); 
    } 
    static void Main(string[] args) { 
     Action curried = Curry(Foo, 5); 
     curried(); 
    } 
} 

Rõ ràng phương pháp Foo tương ứng với phương pháp của bạn Foo, chỉ với các cuộc gọi thích hợp để Console.WriteLine thay vì std::cout.

Tiếp theo, chúng tôi khai báo phương thức Curry chấp nhận số Action<T> và trả lại Action. Nói chung, một số Action<T> là đại biểu chấp nhận một thông số duy nhất của loại T và trả lại void. Cụ thể, FooAction<int> vì nó chấp nhận một thông số loại int và trả về void. Đối với loại trả về là Curry, nó được khai báo là Action. An Action là đại biểu không có tham số và trả về void.

Định nghĩa của Curry khá thú vị. Chúng tôi đang xác định một hành động bằng cách sử dụng một biểu thức lambda mà là một hình thức rất đặc biệt của một đại biểu vô danh. Hiệu quả

() => action(parameter) 

nói rằng tham số void được ánh xạ tới action đánh giá ở parameter.

Cuối cùng, trong Main chúng ta đang khai báo một thể hiện của Action tên curried đó là kết quả của việc áp dụng Curry để Foo với tham số 5. Điều này đóng vai trò giống như bind(fun_ptr(foo), 5) trong ví dụ C++ của bạn.

Cuối cùng, chúng tôi gọi đại biểu mới được thành lập curried thông qua cú pháp curried(). Điều này giống như someCallback() trong ví dụ của bạn.

Thuật ngữ ưa thích cho điều này là currying.

Như một ví dụ thú vị hơn, hãy xem xét những điều sau đây:

class Program { 
    static Func<TArg, TResult> Curry<TArg, TResult>(
     Func<TArg, TArg, TResult> func, 
     TArg arg1 
    ) { 
     return arg => func(arg1, arg); 
    } 

    static int Add(int x, int y) { 
     return x + y; 
    } 

    static void Main(string[] args) { 
     Func<int, int> addFive = Curry<int, int>(Add, 5); 
     Console.WriteLine(addFive(7)); 
    } 
} 

Ở đây chúng ta đang tuyên bố một phương pháp Curry chấp nhận một đại biểu (Func<TArg, TArg, TResult> chấp nhận hai tham số của cùng một loại TArg và trả về một giá trị của một số khác gõ TResult và một tham số kiểu TArg và trả về một đại biểu chấp nhận một tham số duy nhất của loại TArg và trả về một giá trị kiểu TResult (Func<TArg, TResult>).

Sau đó, như một thử nghiệm, chúng tôi khai báo một phương thức Add chấp nhận hai tham số kiểu int và trả về một tham số kiểu int (một Func<int, int, int>). Sau đó, trong Main, chúng tôi khởi tạo đại biểu mới có tên addFive hoạt động như một phương thức thêm năm tham số đầu vào của nó. Do đó

Console.WriteLine(addFive(7)); 

in 12 trên bảng điều khiển.

+0

Cảm ơn câu trả lời chi tiết của bạn. Tôi đánh dấu câu trả lời khác là câu trả lời được chấp nhận vì câu trả lời ngắn gọn hơn, mặc dù nó thiếu một số chi tiết quan trọng mà bạn đã đưa vào. – Catskul

+0

Chắc chắn. Tôi chỉ hy vọng màu sắc bổ sung sẽ giúp. :-) – jason

+0

Câu trả lời vô cùng hữu ích, điều này. Giới thiệu một khái niệm tuyệt vời, currying, một cách dễ hiểu. Chắc chắn sẽ thêm điều này vào hộp công cụ tinh thần của tôi. –

4

Hãy thử như sau

class Example { 
    static void foo(int i) { 
    Console.WriteLine(i); 
    } 
    public static void Main() { 
    Action someCallback =() => foo(5); 
    someCallback(); 
    } 
} 

Hoặc cho một cái gì đó thậm chí gần gũi hơn với C++ phần truy cập

class Example { 
    static void foo(int i) { 
    Console.WriteLine(i); 
    } 
    static Action bind<T>(Action<T> action, T value) { 
    return() => action(value); 
    } 
    public static void Main() { 
    Action someCallback = bind(foo, 5); 
    someCallback(); 
    } 
} 

Giải thích. Điều đang xảy ra ở đây là tôi đang tạo ra một đại biểu mới bằng một biểu thức lambda. Lambda là biểu thức bắt đầu bằng () =>. Trong trường hợp này nó tạo ra một đại biểu chấp nhận không có đối số và không tạo ra giá trị. Nó tương thích với loại Action.

+0

Woah. Hấp dẫn. Tôi đang thấy gì ở đây? – Catskul

+2

Anh ấy đang tạo một đại biểu mới bằng cách sử dụng biểu thức lambda. Người đại diện gọi "foo (5)". Nó giống như thực hiện một phương pháp mới trên bay để gọi foo (5) cho bạn, sau đó gán nó cho đại biểu someCallback. –

+0

'Hành động ' là một đại biểu được cung cấp bởi khuôn khổ. Đó là một hàm lấy một tham số (kiểu T) và không trả về gì (void). Anh ta gán một hàm ẩn danh cho đại biểu đó (mà anh ta đã đặt tên là 'someCallback'). Các parens trống chỉ ra rằng nó không có đối số, biểu thức sau '=>' là phần thân của hàm. – Sapph

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