Делегаты в С# предлагают аналогичную функциональность, как указатели на функцию в C. Я слышал, как кто-то сказал, что "делегаты С# на самом деле лучше, чем указатели на функции в C". Как так? Пожалуйста, объясните с примером.
Как делегаты в С# лучше, чем указатели на функции в C/С++?
Ответ 1
"Лучше" субъективно - но главными отличиями являются:
- Введите безопасность. Делегату не только гарантированно ссылаться на действительный метод, он гарантированно относится к методу с правильной сигнатурой.
- Это указатель связанного метода, то есть делегат может указывать на конкретный объект, на который можно вызвать делегат. Таким образом, делегат
Action<string>
может ссылаться наalice.GetName
илиbob.GetName
, а не только наPerson.GetName
. Это может быть похоже на С++ "указатель на член" - я не уверен.
Кроме того, язык С# поддерживает закрытие через делегирование анонимным методам и лямбда-выражениям - например, захват локальных переменных процедуры объявления, которые делегат может ссылаться при последующем их выполнении. Это, строго говоря, не функция делегатов - она включена компилятором С#, делающим магию для анонимных методов и лямбда-выражений, - но это все еще стоит упомянуть, потому что это позволяет много функциональных идиом в С#.
РЕДАКТИРОВАТЬ: Как отмечает CWF в комментариях, еще одно возможное преимущество делегатов С# заключается в том, что объявления типов делегатов легче читать многим. Разумеется, это может быть вопрос знакомства и опыта.
Ответ 2
Указатели всегда могут указывать на неправильное место:). Я могу указать на не-функцию или произвольное место в памяти.
Но с точки зрения функциональности указатели на функции могут делать все, что могут сделать делегаты.
Ответ 3
Одна вещь, которую делегат обеспечивает, что указатель на функцию C/С++ не является безопасностью типа. То есть в C/С++ вы можете перетащить указатель функции в переменную-указатель функции, объявленную с неправильной сигнатурой функции (или даже int double или хуже с соответствующим уговором), и компилятор будет рад создать код, который вызывает функция полностью неверна. В С# сигнатура типа должна соответствовать сигнатуре типа делегата, а также тому, как в конечном итоге вызывается делегат.
Ответ 4
Многие люди называют делегатов С# более "безопасными по типу", чем указатели на функции С++, и я действительно нахожу его вводящим в заблуждение. В действительности они не являются более безопасными для типов, поскольку указатели на функции С++. Пример кода С++ (скомпилированный 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;
}
Итак, как видно из приведенного выше примера, если вы не используете "грязные хаки" для "заткновения" компилятора, легко обнаруживается несоответствие типов, и сообщение компилятора легко понять. Так что это безопасность типа, насколько я понимаю.
Относительно "ограниченности" указателей на функции-члена. Действительно, в С++ указатель-член не связан, указатель на функцию-член должен применяться к type, которая соответствует сигнатуре указателя участника. Пример:
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
}
Опять же, все абсолютно безопасно.