2010-12-19 14 views
58

Hoy estaba pensando en declarar esto:La creación de delegados manualmente vs utilizando acción de los delegados/Func

private delegate double ChangeListAction(string param1, int number); 

pero ¿por qué no usar esta:

private Func<string, int, double> ChangeListAction; 

o si ChangeListAction no tendría ningún valor de retorno que pude utilizar:

private Action<string,int> ChangeListAction; 

Entonces, ¿dónde está la ventaja en declarar un delegado con el delegate palabra clave?

¿Es por .NET 1.1, y con .NET 2.0 llegó Action<T> y con .NET 3.5 llegó Func<T>?

Respuesta

46

La ventaja es la claridad. Al darle al tipo un nombre explícito, es más claro para el lector lo que hace.

También lo ayudará cuando escriba el código. Un error como este:

cannot convert from Func<string, int, double> to Func<string, int, int, double> 

es menos útil que uno que dice:

cannot convert from CreateListAction to UpdateListAction 

También significa que si usted tiene dos delegados diferentes, los cuales tienen los mismos tipos de parámetros, pero conceptualmente hacen dos cosas completamente diferentes, el compilador puede asegurarse de que no se pueda usar accidentalmente uno que signifique el otro.

+1

El Func tiene un nombre como Func así que si el compilador no dice: No se puede convertir de Func1Name a Func2Name, entonces NO sería menos útil. – Elisabeth

10

Declarar un delegado explícitamente puede ayudar con algunas comprobaciones de tipo. El compilador puede asegurarse de que el delegado asignado a la variable se use como ChangeListAction y no de alguna acción aleatoria que resulte ser compatible con la firma.

Sin embargo, el valor real de declarar su propio delegado es que le da un significado semántico. Una persona que lee el código sabrá lo que el delegado está haciendo por su nombre. Imagínese si tuviera una clase con tres campos int pero, en su lugar, declaró una matriz de tres elementos int. La matriz puede hacer lo mismo pero los nombres de los campos brindan información semántica que es útil para los desarrolladores.

Debe usar delegados Func, Predicado y Acción cuando diseña una biblioteca de propósito general como LINQ. En este caso, los delegados no tienen una semántica predefinida que no sea el hecho de que ejecutarán y actuarán o se usarán como un predicado.

En una nota al margen hay un problema de equilibrio similar con Tuple vs anonymous type vs declarar su propia clase. Podrías simplemente pegar todo en una Tupla pero luego las propiedades son solo Item1, Item2 que no dice nada sobre el uso del tipo.

5

Declare explícitamente al delegado cuando comience a obtener demasiados parámetros en Func/Action, de lo contrario, tendrá que volver a mirar hacia atrás, "¿Qué significa la segunda int nuevamente?"

7

Como algunas respuestas mencionan, la victoria es claridad, usted nombra el tipo y será más fácil de entender para un usuario de su api. Diría que, en la mayoría de los casos, declaro los tipos de delegados para sus apis públicas, pero está bien usar Func<?,?> internamente.

Una gran ventaja de declarar el tipo de delegado que no se menciona en las otras respuestas es que, aparte de darle un nombre al nombre, también se pueden nombrar los parámetros, esto aumentará enormemente la usabilidad.

59

La llegada de las familias de delegados Action y Func ha hecho que los delegados personalizados sean menos utilizados, pero este último todavía encuentra usos. Ventajas de los delegados personalizados incluyen:

  1. Como otros han señalado, transmite la intención clara diferencia genérica Action y Func (Patrik tiene un muy buen punto acerca de los nombres de los parámetros significativos).

  2. Puede especificar ref/out parámetros a diferencia de los otros dos delegados genéricos. Por ejemplo, puede tener

    public delegate double ChangeListAction(out string p1, ref int p2); 
    

    pero no

    Func<out string, ref int, double> ChangeListAction; 
    
  3. Además, con delegados personalizados que necesita para escribir ChangeListAction (me refiero a la definición) sólo una vez en algún lugar de su base de código, mientras que si se no defina uno, tendrá que ensuciar en todas partes Func<string, int, double> por todas partes. Cambiar la firma será una molestia en este último caso, un mal caso de no estar seco.

  4. Puede tener parámetros opcionales.

    public delegate double ChangeListAction(string p1 = "haha", int p2); 
    

    pero no

    Func<string, int, double> ChangeListAction = (p1 = "haha", p2) => (double)p2; 
    
  5. Puede tener params palabra clave para los parámetros de un método, no así con Action/Func.

    public delegate double ChangeListAction(int p1, params string[] p2); 
    

    pero no

    Func<int, params string[], double> ChangeListAction; 
    
  6. Bueno, si usted está realmente fuera de suerte y necesita más de 16 parámetros (por el momento) :)


En cuanto a los méritos de Action y Func:

  1. Es rápido y sucio, y lo uso por todas partes. Hace que el código sea corto si el caso de uso es trivial (los delegados personalizados han pasado de moda conmigo).

  2. Más importante aún, su tipo es compatible en todos los dominios. Action y Func están definidos en el marco, y funcionan perfectamente siempre que los tipos de parámetros coincidan. No puede tener ChangeSomeAction para ChangeListAction. Linq encuentra un gran uso de este aspecto.

+1

Aquí hay otro en el que no puede usar Func: no puede usar Func si tiene que devolverse tal como se menciona aquí: http://stackoverflow.com/questions/27989296/how-do-i-declare-a-func-delegate- which-returns-a-func-delegate-of-the-same-type – Marwie

+0

gracias @Marwie. Servicial. Se agregará a mi respuesta en algún momento. – nawfal

+0

En el ejemplo # 5 'params' debe ser el último parámetro. – Jalal

6

he encontrado un caso de uso especial donde sólo se puede utilizar delegado:

public delegate bool WndEnumProc(IntPtr hwnd, IntPtr lParam); 
[DllImport("User32.dll")] 
public static extern bool EnumWindows(WndEnumProc lpEnumFunc, IntPtr lParam); 

con la tecla FUNC/Acción simplemente no funciona: 'Namespace.Class.WndEnumProc' is a 'field' but is used like a 'type':

public Func<IntPtr, IntPtr, bool> WndEnumProc; 
[DllImport("User32.dll")] 
public static extern bool EnumWindows(WndEnumProc lpEnumFunc, IntPtr lParam); 

El siguiente código compila, pero tira s excepción cuando se ejecuta porque System.Runtime.InteropServices.DllImportAttribute no admite el cálculo de referencias de tipos genéricos:

[DllImport("User32.dll")] 
public static extern bool EnumWindows(Func<IntPtr, IntPtr, bool> lpEnumFunc, IntPtr lParam); 

presento este ejemplo para mostrar a todo el que: a veces delegado es su única opción. Y esto es una respuesta razonable a su pregunta why not use Action<T>/Func<T> ?

0

Como se ha dicho MSDN, Func<> está a su vez predefinido Delegate. Por primera vez, me confundí acerca de esto. Después de lo experimental, mi comprensión fue bastante más clara. Normalmente, en C#, podemos ver

Type como un puntero a Instance.

El mismo concepto se aplica a

Delegate como un puntero a Method

La diferencia entre estos a las cosas es Delegate no poseen el concepto de programación orientada a objetos, por ejemplo, Inheritance . Para hacer más clara esta cosas, hice lo experimental con

public delegate string CustomDelegate(string a); 

// Func<> is a delegate itself, BUILD-IN delegate 
//========== 
// Short Version Anonymous Function 
//---------- 
Func<string, string> fShort = delegate(string a) 
{ 
    return "ttt"; 
}; 
// Long Version Anonymous Function 
//---------- 
Func<string, string> fLong = a => "ttt"; 

MyDelegate customDlg; 
Func<string, string> fAssign; 
// if we do the thing like this we get the compilation error!! 
// because fAssign is not the same KIND as customDlg 
//fAssign = customDlg; 

Muchos métodos construir-en en marco (por ejemplo, LINQ), recibirá el parámetro de Func<> delegado. Lo que podemos hacer con este método es

Declare el delegado de Func<> tipo y pasarlo a la función en lugar de Define el delegado personalizado.

Por ejemplo, en el código anterior añado más código

string[] strList = { "abc", "abcd", "abcdef" }; 
strList.Select(fAssign); // is valid 
//strList.Select(customDlg); // Compilation Error!! 
1

Para una mejor y más elaborada aspecto respuesta a @nawfal. Trataré de ser más simplista.

Usted está declarando miembro de una clase, por lo que debe quedarse con el delegado. El uso de delegate es más descriptivo y estructural.

Action/Func Los tipos están hechos para circular, por lo que debe usarlos más como parámetros y variables locales.

Y en realidad ambos están heredando la clase Delegate. Action y Func son tipos genéricos y simplifican la creación de delegados con diferentes tipos de parámetros. Y la palabra clave delegar realmente crea una clase completamente nueva que hereda del Delegado en una declaración.

Cuestiones relacionadas