2009-06-30 13 views
6

Necesito escribir una función de delegado que pueda "ajustar" algo mientras/try/catch code alrededor de una llamada UDP básica para verificar el enlace. Lo hice funcionar para Func para una función que no tiene argumentos, pero no puedo hacer que funcione para Action, que tiene un argumento (pero no retorno). Parece que no puedo pasar la discusión de una manera lógica sin que el compilador se queje.Usando la acción <T> como argumento en C# (imitando un puntero a la función)

¿Estoy hablando de algo así? Soy nuevo en C# y esencialmente estoy tratando de imitar la idea de un puntero a la función. ¿No debería sobrecargar esta función? Sé que no se puede sobrecargar a los delegados (supongo que es por eso que Func y Action existen).

Esto funciona:

protected TResult udpCommand<TResult>(Func<TResult> command) 
     { 
      TResult retValue = default(TResult); 
      while (!linkDownFail) 
      { 
       try 
       { 
        retValue = command(); 
        break; 
       } 
       catch 
       { 
        LinkStateCallBack(ip, getLinkStatus()); 
        if (linkDownFail) throw new LinkDownException(); 
        Thread.Sleep(100); 
       } 
      } 
      return retValue; 
     } 

Pero esto no es así:

protected void udpCommand<T>(Action<T> command(T value)) 
     { 
      while(!linkDownFail) 
      { 
       try 
       { 
        command(value); 
        break; 
       } 
       catch 
       { 
        LinkStateCallBack(ip, getLinkStatus()); 
        if (linkDownFail) throw new LinkDownException(); 
        Thread.Sleep(100); 
       } 
      } 
      return; 
     } 

convención de llamada (por uno que funcione):

udpCommand<uint>(someUdpCommand); 
+2

Adicional: en realidad no necesita la T; puedes simplemente tomar 'Action' (no arg), y llamar como: udpCommand (() => SomeMethod (123)); –

+0

¿Esto me permitiría tener un número variable de parámetros de entrada para udpCommand sin sobrecargarlo para, digamos, hasta 4 parámetros? Eso es lo que necesito hacer. ¿Todavía necesitaría un Func y una Acción? – cgyDeveloper

Respuesta

10

Si desea que esto sea lo suficientemente genérico como para manejar cualquier número de argumentos , trate de usar la acción no genernic delegado:

protected void udpCommand(Action command) 
{ 
    while(!linkDownFail) 
    { 
     try 
     { 
      command(); 
      break; 
     } 
     catch 
     { 
      LinkStateCallBack(ip, getLinkStatus()); 
      if (linkDownFail) throw new LinkDownException(); 
      Thread.Sleep(100); 
     } 
    } 
    return; 
} 

En C# 3.0, se le puede llamar así:

udpCommand(() => noParameterMethod()); 
udpCommand(() => singleParameterMethod(value)); 
udpCommand(() => manyParameterMethod(value, value2, value3, value4)); 

En C# 2.0 que es un poco más feo:

udpCommand(delegate { noParameterMethod(); }); 
udpCommand(delegate { singleParameterMethod(value); }); 
udpCommand(delegate { manyParameterMethod(value, value2, value3, value4); }); 

Esto le da ejecución diferida sin ti bloqueo en una firma de método en particular.

EDITAR

acabo cuenta de que un poco robado el comentario de Marc Gravell ... Disculpas Marc. Para responder a cómo puede reducir su duplicación, puede hacer que el método Action llamar al método Func<T>, así:

protected void udpCommand(Action command) 
{ 
    udpCommand(() => { command(); return 0; }); 
} 

creo (y puedo estar equivocado) que la devolución de 0 no es más costoso que (implícitamente) volviendo vacío, pero puedo estar lejos de aquí.Incluso si tiene un costo, solo pondría una pequeñita y pequeña snoodge extra en la pila. En la mayoría de los casos, el costo adicional nunca le causará ningún dolor.

+0

Me encanta este lugar. Justo lo que necesitaba como novato de C#/VS.NET. Gracias. – cgyDeveloper

2

Creo que sólo necesita sacar el (valor T) después de 'comando'.

+0

El compilador se queja con o sin el T. – cgyDeveloper

+0

Hmmm Creo que porque la pregunta era un poco ambigua, pero debería haberme dado cuenta de que también intentaba pasar el argumento a la Acción. Estoy de acuerdo con la solución provista en su Respuesta Aceptada, que simplemente pasa también en un valor T (aunque no entre paréntesis). Si tuviera 'valor' disponible dentro del bloque del código, entonces creo que hubiera funcionado bien. –

+0

Ah, sí, creo que eso funcionaría también. Gracias por la respuesta. – cgyDeveloper

4

Qué quiere decir:

protected void udpCommand<T>(Action<T> command, T value) {...} 

con llamadas:

udpCommand(someUdpCommand, arg); 

Tenga en cuenta que esto puede funcionar mejor en C# 3.0, que tiene fuerte inferencia de tipo genérico que C# 2.0.

+0

Aha! Este parece funcionar ... al menos el compilador ya no se queja. Los ejemplos de Microsoft realmente no cubrieron mi caso de uso. Afortunadamente estoy usando C# 3.0. El kilometraje puede variar en C# 2.0. – cgyDeveloper

0

¿Estás tratando de hacer esto ...

protected void udpCommand<T>(Action<T> command, T value) 
{ 
    while(!linkDownFail) 
    { 
    try     
    { 
     command(value); 
     // etc. 
    } 
    } 
} 

Entonces sería trabajar así ...

public void ActionWithInt(int param) 
{ 
    // some command 
} 

Action<int> fp = ActionWithInt; 

udpCommand<int>(fp, 10); // or whatever. 
Cuestiones relacionadas