2010-03-29 28 views
13

Các đại biểu trong C# cung cấp chức năng tương tự như các con trỏ hàm trong C. Tôi nghe ai đó nói "Các đại biểu C# thực sự tốt hơn các con trỏ hàm trong C". Làm thế nào mà? Hãy giải thích với một ví dụ.Các đại biểu trong C# tốt hơn các con trỏ hàm trong C/C++ như thế nào?

+0

Ai nói điều đó có thể giải thích rõ hơn ý nghĩa của chúng bằng cách đó, phải không? – Victor

+0

Thực ra tôi đã đọc nó trong một cuốn sách từ lâu. Tôi không nhớ cuốn sách nào. Nhưng bây giờ tôi đang sử dụng con trỏ hàm trong mã C của tôi. Vì vậy, chỉ cần nhớ. – pecker

+0

Nó được đánh máy mạnh mẽ, loại dữ liệu lớp học đầu tiên (như lớp) và bạn có thể xác định đại biểu nội tuyến là tốt. Đây là một số tiến bộ cho hàm con trỏ trong C/C++ –

Trả lời

14

"Better" là chủ quan - nhưng sự khác biệt chính là:

  • Loại an toàn. Một đại biểu không chỉ được bảo đảm để tham khảo một phương pháp hợp lệ, nó được đảm bảo để đề cập đến một phương pháp với chữ ký chính xác.
  • Đó là con trỏ phương thức bị ràng buộc - nghĩa là, người được ủy quyền có thể trỏ đến một đối tượng cụ thể để gọi đại biểu. Do đó, một đại diện của Action<string> có thể tham chiếu đến alice.GetName hoặc bob.GetName thay vì chỉ Person.GetName. Điều này có thể tương tự như C++ "con trỏ đến thành viên" - Tôi không chắc chắn.

Ngoài ra, ngôn ngữ C# hỗ trợ đóng cửa thông qua các đại biểu cho các phương thức ẩn danh và biểu thức lambda - tức là nắm bắt các biến cục bộ của quy trình khai báo mà đại biểu có thể tham chiếu khi sau này được thực hiện. Đây không phải là một tính năng của các đại biểu - nó được kích hoạt bởi trình biên dịch C# làm một số phép thuật trên các phương thức nặc danh và các biểu thức lambda - nhưng nó vẫn đáng nhắc đến vì nó cho phép rất nhiều thành phần chức năng trong C#.

EDIT: Như CWF ghi chú trong nhận xét, một ưu điểm khác của đại biểu C# là việc khai báo kiểu ủy nhiệm dễ đọc hơn. Đây có thể là vấn đề về sự quen thuộc và kinh nghiệm, tất nhiên.

+0

Loại An toàn: Không phải là trường hợp với tất cả các loại con trỏ trong C/C++? Nó không phải là vấn đề của trình biên dịch chúng tôi đang sử dụng? Tôi có nghĩa là, nếu trình biên dịch của tôi ném một lỗi nếu tôi trỏ một con trỏ hàm vào int. Vậy thì điều đó có an toàn không? – pecker

+0

Không, bởi vì bạn có thể khai báo một con trỏ đến một 'int f (int)', đặt nó vào địa chỉ của một 'int f (int)', sau đó đưa nó vào một 'char * f (long)'. Hoặc 'void * badptr =" rác ngẫu nhiên "; INT_FN ohno = (INTFN) badptr; int pwned = ohno (123); '(trong đó INTFN là một typedef cho' int f (int) ', tôi không thể nhớ cú pháp khai báo C++ \ * grin \ *). Trong khi C# sẽ thất bại các diễn viên ác. – itowlson

+2

An toàn loại là vấn đề ngôn ngữ nhiều hơn tính năng ngôn ngữ cụ thể. Điều đó nói rằng, chỉ nghĩ rằng bạn còn lại là cú pháp. Khai báo con trỏ hàm trong C (và có lẽ C++ khi không sử dụng Boost) có thể khó làm sáng tỏ. Nhưng tuyên bố ủy nhiệm rất dễ dàng. Ngoài ra, lambdas là một trường hợp đặc biệt của các đại biểu (tôi nghĩ), và đôi khi họ rất rất hữu ích. – CWF

4

Con trỏ luôn có thể trỏ đến địa điểm sai :) I.e nó có thể trỏ đến một không có chức năng hoặc một vị trí tùy ý trong bộ nhớ.

Nhưng về chức năng, các con trỏ hàm có thể làm bất cứ điều gì mà các đại biểu có thể làm.

+3

Chỉ cần lưu ý rằng thật khó để vượt qua mức độ lớn của một giao dịch này. Đó là một con trỏ hàm C++ vẫn chỉ là một con trỏ sâu xuống cho phép tất cả các loại badness trong việc lừa một điểm để trỏ tới mã độc. Một lỗi tràn bộ đệm không đáng kể có thể đột nhiên trở nên dễ khai thác hơn nhiều. –

+0

Joel, không tranh luận với bạn ở đó! Tôi nghĩ rằng đó là lợi thế chính của Java so với C++ - loại bỏ các con trỏ có nghĩa là loại bỏ toàn bộ hàng loạt; –

3

Một điều mà một đại biểu cung cấp rằng con trỏ hàm C/C++ không phải là loại an toàn. Tức là, trong C/C++, bạn có thể chèn một con trỏ hàm vào một biến con trỏ hàm được khai báo với chữ ký hàm sai (hoặc thậm chí là một int đôi hoặc tệ hơn với coaxing thích hợp), và trình biên dịch sẽ rất vui khi tạo mã mà các cuộc gọi chức năng hoàn toàn không chính xác. Trong C#, chữ ký kiểu của hàm phải khớp với chữ ký kiểu của ủy nhiệm và cũng là cách mà người được gọi cuối cùng.

+0

Đó không phải là trường hợp với tất cả các loại con trỏ trong C/C++? Nó không phải là vấn đề của trình biên dịch chúng tôi đang sử dụng? Tôi có nghĩa là, nếu trình biên dịch của tôi ném một lỗi nếu tôi trỏ một con trỏ hàm vào int. Vậy thì điều đó có an toàn không? – pecker

+0

Vâng, chắc chắn rồi. Trình biên dịch của bạn có thể cảnh báo bạn khi bạn tình cờ cố gắng đặt thứ gì đó vào một khe mà nó không phù hợp. Tôi sẽ không cho rằng bạn không nên sử dụng con trỏ hàm nếu bạn đang viết mã bằng C++ - đôi khi đó chỉ là ngôn ngữ phù hợp với những gì bạn đang làm. – sblom

1

Nhiều người đề cập đến các đại biểu C# càng an toàn hơn loại C++ và tôi thực sự thấy nó gây hiểu nhầm. Trong thực tế, chúng không an toàn hơn với các con trỏ hàm của C++. Một ví dụ C++ (biên soạn bởi MSVS 2005 SP1):

typedef int (*pfn) (int); 

int f (int) { 
    return 0; 
} 

double d (int) { 
    return 1; 
} 

int main() 
{ 
    pfn p=f; // OK 
    p=d; // error C2440: '=' : cannot convert from 'double (__cdecl *)(int)' to 'pfn' 
    p=(pfn)d; 
} 

Vì vậy, khi được nhìn thấy từ ví dụ trên trừ khi có ai sử dụng "hacks bẩn" để "im lặng" trình biên dịch các loại không phù hợp có thể dễ dàng phát hiện và trình biên dịch của tin nhắn dễ hiểu. Vì vậy, đó là loại an toàn như tôi hiểu nó.

Về "độ liên kết" của các con trỏ hàm thành viên. Thật vậy, trong C++ pointer-to-member không bị ràng buộc, con trỏ hàm thành viên phải được áp dụng cho một biến kiểu khớp với chữ ký của con trỏ thành viên. Ví dụ:

class A { 
public: 

    int f (int) { 
     return 2; 
    } 

}; 

typedef int (A::*pmfn) (int); 

int main() 
{ 
    pmfn p=&(A::f); 
    // Now call it. 
    A *a=new A; 
    (a->*p)(0); // Calls A::f 
} 

Một lần nữa, mọi thứ hoàn toàn an toàn.

+0

Gõ an toàn cho đến khi bạn truyền tới 'void *' và ngược lại. Sau đó, con trỏ đó sẽ xuất hiện là bất cứ thứ gì bạn muốn, nhưng sẽ phá vỡ khi bạn cố gắng sử dụng nó. C# làm cho thủ đoạn như vậy rất khó khăn, nếu không phải là không thể. – cHao

+1

@cHao nope, C++ không cho phép chuyển đổi ngầm từ void * sang * khác (nó cho phép chuyển đổi ngầm theo hướng khác). Đó là C bạn đang nói về. Để làm cho khó khăn như vậy khó khăn (không có cách nào trong địa ngục nó không thể), tôi nghĩ rằng đó là quá mức cần thiết, chỉ cần không làm cho nó dễ dàng để làm do tai nạn và để phần còn lại lên đến lập trình viên (tức là tin tưởng các lập trình viên để sử dụng bộ não của họ). – Giel

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