2010-03-29 13 views
13

Delegaci w C# oferują podobną funkcjonalność co wskaźniki funkcji w C. Słyszałem, że ktoś mówi: "Delegaci C# są w rzeczywistości lepsi niż wskaźniki funkcji w C". Dlaczego? Proszę wyjaśnić na przykładzie.W jaki sposób delegaci w języku C# są lepsze od wskaźników funkcji w C/C++?

+0

Kto powiedział, że może lepiej wyjaśnić, co przez to rozumieją, nie? – Victor

+0

Właściwie to czytam w długiej książce. Nie pamiętam, która książka. Ale teraz używam wskaźników funkcji w moim kodzie C. Tak, właśnie zapamiętany. – pecker

+0

Jest to silnie typowany typ danych pierwszej klasy (jak klasa) i można również zdefiniować delegata wbudowanego. Jest to pewien postęp w kierunku wskaźnika funkcji w C/C++. –

Odpowiedz

14

"Better" jest subiektywna - ale główne różnice to:

  • Typ bezpieczeństwa. Delegat ma nie tylko gwarancję, że odwołuje się do prawidłowej metody, lecz także do metody z prawidłowym podpisem.
  • Jest to wskaźnik metody związany z . Oznacza to, że uczestnik może wskazywać na konkretny obiekt, do którego ma zostać wywołany delegat. Zatem delegat Action<string> może odnosić się do alice.GetName lub bob.GetName, a nie tylko Person.GetName. Ten może być podobny do C++ "wskaźnik do elementu" - nie jestem pewien.

Ponadto język C# obsługuje zamknięcia za pośrednictwem delegatów do metod anonimowych i wyrażeń lambda - tj. Przechwytywanie zmiennych lokalnych procedury deklarowania, które delegat może odwoływać się, gdy zostanie później wykonane. Nie jest to ściśle funkcja delegatów - jest ona włączona przez kompilator C#, który robi magię na anonimowych metodach i wyrażeniach lambda - ale wciąż jest wart wspomnienia, ponieważ umożliwia wiele funkcjonalnych idiomów w języku C#.

EDIT: W CWF zauważa w komentarzach, inny możliwy zaletą C# delegatów jest, że deklaracje typu delegat są łatwiejsze dla wielu ludzi do czytania. Oczywiście może to być kwestia znajomości i doświadczenia.

+0

Bezpieczeństwo typu: Czy nie ma to miejsca w przypadku wszystkich rodzajów wskaźników w języku C/C++? Czy nie chodzi o kompilator, którego używamy? Mam na myśli, jeśli mój kompilator zgłasza błąd, jeśli wskażę wskaźnik funkcji na int. Czy nie byłoby to bezpieczne? – pecker

+0

Nie, ponieważ możesz zadeklarować wskaźnik do 'int f (int)', ustaw go na adres 'int f (int)', a następnie przesuń go do 'char * f (long)'. Lub 'void * badptr =" losowe śmieci "; INT_FN ohno = (INTFN) badptr; int pwned = ohno (123); '(gdzie INTFN jest typedef dla' int f (int) ', po prostu nie pamiętam składni deklaracji C++ \ * grin \ *). Podczas gdy C# zawiedzie złego rzutu. – itowlson

+2

Bezpieczeństwo typu jest bardziej kwestią języka niż określoną cechą języka. To powiedziawszy, jedyną myślą, że pominąłeś, jest składnia. Deklaracje funkcji w C (i prawdopodobnie C++, gdy nie używa się Boost) mogą być trudne do rozwikłania. Ale deklaracje delegatów są łatwe. Ponadto, lambda jest szczególnym przypadkiem delegatów (jak sądzę) i są one czasami bardzo bardzo przydatne. – CWF

4

Wskaźniki zawsze wskazują niewłaściwe miejsce :) Może to wskazywać na niefunkcjonalne lub arbitralne miejsce w pamięci.

Ale pod względem funkcjonalności wskaźniki funkcji mogą robić wszystko, co delegaci mogą zrobić.

+3

Tylko uwaga, że ​​trudno jest przecenić, jak duża jest to transakcja. To, że wskaźnik funkcji C++ jest nadal tylko wskaźnikiem w głąb, pozwala na wszelkiego rodzaju zło pod względem nakłaniania do wskazania złośliwego kodu. W przeciwnym razie niepozorny przepełnienie bufora może nagle stać się znacznie łatwiejsze do wykorzystania. –

+0

Joel, bez kłótni z tobą! Myślę, że to główna zaleta Javy nad C++ - wyeliminowanie wskaźników oznaczało wyeliminowanie całego mnóstwa: roblemów. –

3

Jedna rzecz, którą delegat zapewnia, że ​​wskaźnik funkcji C/C++ nie jest bezpiecznym typem. Oznacza to, że w C/C++ można przesunąć wskaźnik funkcji do zmiennej wskaźnika funkcji zadeklarowanej przy użyciu niewłaściwej sygnatury funkcji (lub nawet podwójnej lub gorszej z odpowiednim koaksowaniem), a kompilator z przyjemnością przygotuje kod wywołujący funkcja całkowicie niepoprawna. W języku C# podpis typu funkcji musi być zgodny z typem podpisu delegata, a także z ostatecznym sposobem wywoływania delegata.

+0

Czy nie jest tak w przypadku wszystkich rodzajów wskaźników w C/C++? Czy nie chodzi o kompilator, którego używamy? Mam na myśli, jeśli mój kompilator zgłasza błąd, jeśli wskażę wskaźnik funkcji na int. Czy nie byłoby to bezpieczne? – pecker

+0

Cóż, na pewno. Twój kompilator może ostrzec cię, gdy przypadkowo spróbujesz umieścić coś w gnieździe, którego nie powinno się zmieścić. Nie twierdzę, że nie powinieneś używać wskaźników funkcji, jeśli piszesz kod w C++ - czasami jest to właściwy język do tego, co robisz. – sblom

1

Wiele osób odwołuje się do delegatów C# jako bardziej "bezpiecznych" niż do wskaźników funkcyjnych C++ i naprawdę uważam, że jest to mylące. W rzeczywistości nie są bardziej bezpieczne dla typów, ponieważ są wskaźnikami funkcji C++. Przykładem C++ kod (skompilowane przez 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; 
} 

Więc jak widać z powyższego przykładu, chyba że ktoś używa „brudnych hacków” do „shut up” kompilator niedopasowanie typu jest łatwy do wykrycia i kompilator wiadomość jest łatwa do zrozumienia. To jest bezpieczeństwo typu, jak rozumiem.

Odnośnie do "granicy" wskaźników funkcji pręta. Rzeczywiście, w C++ wskaźnik do elementu nie jest powiązany, wskaźnik funkcji elementu musi być zastosowany do zmiennej typu , która pasuje do sygnatury wskaźnika elementu. Przykład:

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 
} 

Znowu wszystko jest idealnie bezpieczne.

+0

Wpisz bezpieczny, aby rzucić do "void *" iz powrotem. Wtedy ten wskaźnik będzie wyglądał tak, jak chcesz, ale pęknie, gdy spróbujesz go użyć. C# sprawia, że ​​takie oszustwo jest bardzo trudne, jeśli nie niemożliwe. – cHao

+1

@CHao nope, C++ nie zezwala na niejawną konwersję z void * na inną * (umożliwia niejawną konwersję w innym kierunku). To C, o którym mówisz. Co do tego, że takie sztuczki są trudne (nie ma mowy, że w piekle jest to niemożliwe), myślę, że to przesada, po prostu nie rób tego przez przypadek i pozostaw resztę programistom (tj. Ufaj, że programiści używają swojego mózgu). – Giel

Powiązane problemy