2010-03-29 18 views
13

Los delegados en C# ofrecen una funcionalidad similar a los punteros de función en C. Escuché a alguien decir "los delegados de C# en realidad son mejores que los punteros de función en C". ¿Cómo? Por favor explique con un ejemplo.¿Cómo son mejores los delegados en C# que los punteros de función en C/C++?

+0

Quien haya dicho eso puede explicar mejor lo que quieren decir con eso, ¿no? – Victor

+0

En realidad, lo leí en un libro largo tiempo atrás. No recuerdo qué libro. Pero ahora estoy usando indicadores de función en mi código C. Entonces, acabo de recordar. – pecker

+0

Está fuertemente tipado, tipo de datos de primera clase (como clase) y también puede definir delegados en línea. Estos son algunos avances para el puntero de función en C/C++ –

Respuesta

14

"mejor" es subjetiva - pero las principales diferencias son:

  • Seguridad de tipos. No solo se garantiza que un delegado se refiera a un método válido, sino que se garantiza que se referirá a un método con la firma correcta.
  • Es un puntero de método enlazado, es decir, el delegado puede señalar a un objeto específico al que llamar al delegado. Por lo tanto, un delegado Action<string> podría consultar alice.GetName o bob.GetName en lugar de solo Person.GetName. Este podría ser ser similar a C++ "puntero a miembro" - No estoy seguro.

Además, el lenguaje C# admite cierres a través de delegados a métodos anónimos y expresiones lambda, es decir, capturar variables locales del procedimiento de declaración, que el delegado puede consultar cuando se ejecute posteriormente. Esto no es estrictamente una característica de los delegados, está habilitado por el compilador de C# que hace algo de magia en métodos anónimos y expresiones lambda, pero vale la pena mencionarlo porque habilita muchos de los modismos funcionales en C#.

EDIT: Como señala CWF en los comentarios, otra posible ventaja de delegados C# es que las declaraciones de tipo delegado son más fáciles de leer para mucha gente. Esto puede ser una cuestión de familiaridad y experiencia, por supuesto.

+0

Tipo de seguridad: ¿No es así con todo tipo de punteros en C/C++? ¿No es la cuestión del compilador que estamos usando? Quiero decir, si mi compilador arroja un error si apunto un puntero a la función int. Entonces, ¿no sería eso seguro? – pecker

+0

No, porque podría declarar un puntero a 'int f (int)', establecerlo a la dirección de un 'int f (int)', luego convertirlo a 'char * f (long)'. O 'void * badptr =" basura aleatoria "; INT_FN ohno = (INTFN) badptr; int pwned = ohno (123); '(donde INTFN es un tipodef para' int f (int) ', simplemente no puedo recordar la sintaxis de la declaración de C++ \ * grin \ *). Mientras que C# fallará el lanzamiento malvado. – itowlson

+2

La seguridad de tipo es más un problema del idioma que la característica de idioma en particular. Dicho eso, lo único que se puede omitir es la sintaxis. Las declaraciones de punteros de función en C (y probablemente en C++ cuando no se utiliza Boost) pueden ser difíciles de descifrar. Pero las declaraciones de delegados son fáciles. Además, las lambdas son un caso especial de delegados (creo), y a veces son muy útiles. – CWF

4

Los punteros siempre pueden señalar el lugar equivocado :) Es decir, puede apuntar a un lugar no funcional o arbitrario en la memoria.

Pero en términos de funcionalidad, los punteros a las funciones pueden hacer cualquier cosa que los delegados puedan hacer.

+3

Solo una nota de que es difícil exagerar qué tan grande es esto. Que un puntero de función C++ sea solo un puntero en el fondo permite todo tipo de maldad en términos de engañar a uno para que apunte a un código malicioso. De otro modo, un desbordamiento de búfer sin importancia puede ser mucho más fácil de explotar. –

+0

Joel, no hay discusión contigo allí! Creo que esa es la principal ventaja de Java sobre C++: eliminar punteros significaba eliminar una gran cantidad de; problemas. –

3

Una cosa que un delegado proporciona que un puntero de función C/C++ no es tipo seguridad. Es decir, en C/C++, puede insertar un puntero de función en una variable de puntero de función declarada con la firma de función incorrecta (o incluso una int a doble o peor con un engatillado apropiado), y el compilador estará feliz de producir el código que llama la función completamente incorrecta. En C#, la firma de tipo de la función debe coincidir con la firma de tipo del delegado y también la forma en que finalmente se llama al delegado.

+0

¿No es así con todo tipo de punteros en C/C++? ¿No es la cuestión del compilador que estamos usando? Quiero decir, si mi compilador arroja un error si apunto un puntero a la función int. Entonces, ¿no sería eso seguro? – pecker

+0

Bueno, claro. Su compilador puede advertirle cuando trate casualmente de poner algo en una ranura que no encaje. No argumentaría que no deberías usar punteros a función si estás escribiendo código en C++; a veces ese es el lenguaje correcto para lo que estás haciendo. – sblom

1

Muchas personas se refieren a delegados de C# como más "seguro en cuanto a tipos" que los punteros de función de C++ y realmente me parece engañoso. En realidad, no son más seguros con respecto a los tipos que los punteros de función de C++. Un ejemplo código C++ (compilado por 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; 
} 

Así como se ve en el ejemplo anterior si no se utiliza "trucos sucios" que "se calle" el compilador es fácil de detectar el tipo de desajuste y el compilador de el mensaje es fácil de entender Entonces eso es seguridad de tipo tal como lo entiendo.

En cuanto a la "encuadernación" de los punteros de funciones miembro. De hecho, en C++ puntero-a-miembro no está vinculado, el puntero de función miembro debe aplicarse a una variable de tipo que coincida con la firma del puntero miembro. Un ejemplo:

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 
} 

Nuevamente, todo es perfectamente seguro.

+0

Escribe seguro hasta que lo conviertas en un 'vacío *' y regresa. Entonces ese puntero parecerá ser lo que quieras, pero se romperá cuando trates de usarlo realmente. C# hace que ese engaño sea muy difícil, si no imposible. – cHao

+1

@cHao no, C++ no permite la conversión implícita de vacío * a otro * (permite la conversión implícita en la otra dirección). Eso es C de lo que estás hablando. En cuanto a dificultar tales engaños (de ninguna manera es imposible), creo que es exagerado, simplemente no lo haga fácil por accidente y deje el resto al programador (es decir, confíe en que los programadores usen su cerebro) – Giel

Cuestiones relacionadas