2009-06-22 14 views
5

Ho due metodi in C# 3.5 che sono identici a una chiamata di una funzione, nello snippet seguente, vedere clientController.GetClientUsername vs clientController.GetClientGraphicalUsernameIn C# 3.5, come si passa il metodo per chiamare un oggetto come parametro

private static bool TryGetLogonUserIdByUsername(IGetClientUsername clientController, string sClientId, out int? logonUserId) 
    { 
     string username; 
     if (clientController.GetClientUsername(sClientId, out username)) 
     { 
      // ... snip common code ... 
     } 

     return false; 
    } 

    private static bool TryGetLogonUserIdByGraphicalUsername(IGetClientUsername clientController, string sClientId, out int? logonUserId) 
    { 
     string username; 
     if (clientController.GetClientGraphicalUsername(sClientId, out username)) 
     { 
      // ... snip common code ... 
     } 

     return false; 
    } 

c'è un modo (delegati, Lamda di?) che possono passare nel quale metodo su clientController voglio chiamare?

Grazie!

+0

L'esempio è un'istanza di un problema generico che ho riscontrato durante il refactoring di blocchi di codice che sono identiche a barre chiamate di metodi diversi. In particolare, mi sembra di vederli nei test. –

risposta

7

Sicuro. Basta definire un delegato in questo modo:

public delegate bool GetUsername(string clientID, out string username); 

E poi passarlo nella vostra funzione e chiamarlo:

private static bool TryGetLogonUserId(IGetClientUsername clientController, string sClientId, out int? logonUserId, GetUsername func) 
{ 
    string username; 
    if (func.Invoke(sClientId, out username)) 
    { 
     // ... snip common code ... 
    } 
    return false; 
} 

Per chiamare la funzione con il delegato, si esegue questa operazione:

TryGetLogonUserId(/* first params... */, clientController.GetClientUsername); 
+2

func.Invoke ... stile scadente ... – leppie

+2

Cosa stai dicendo è in stile povero? Passando nel delegato? Stavo solo rispondendo alla domanda. Chiamando ".Invoke()" sul delegato? Preferisco quella notazione perché chiarisce che stai usando un delegato. –

+2

Sono con Josh G, questa sembra una soluzione abbastanza elegante al problema, e se si nomina "func" con attenzione è chiaro cosa sta succedendo –

0

È possibile passare uno MethodInfo, che può essere controllato staticamente. Tuttavia, sono d'accordo che una riprogettazione può essere richiesta.

private static readonly MethodInfo getRegularLogin = typeof(IGetClientUsername).GetMethod("GetClientUsername"); 
private static bool TryGetLogonUserIdByUsername(IGetClientUsername clientController, string sClientId, out int? logonUserId) 
{ 

    string username; 
    return TryGetLoginReflective(getRegularLogin, clientController, sClientId, out username, out logonUserId); 
} 

private static readonly MethodInfo getGraphicalLogin = typeof(IGetClientUsername).GetMethod("GetClientGraphicalUsername"); 
private static bool TryGetLogonUserIdByGraphicalUsername(IGetClientUsername clientController, string sClientId, out int? logonUserId) 
{ 
    string username; 
    return TryGetLoginReflective(getGraphicalLogin, clientController, sClientId, out username, out logonUserId); 
} 

private static bool TryGetLoginReflective(MethodInfo method, IGetClientUsername clientController, string sClientId, out string username, out int? logonUserId) 
{ 
    object[] args = new object[]{sClientId, null}; 
    if((bool)method.Invoke(clientController, args)) 
    { 
     // ... snip common code ... 
    } 
    logonUserId = ...; 

    username = (string)args[1]; 
    return false; 
} 
+0

Un delegato è un modo molto migliore per fare questo rispetto alla riflessione. –

9

Mentre è possibile passare un delegato come parametro, suggerisco di andare con un percorso diverso. Incapsulare il corpo della dichiarazione if che implica il codice comune in un'altra funzione e chiamarla in entrambe le funzioni.

Visual Studio dispone di una funzione "Refactor -> Extract Method" nel menu di scelta rapida. Puoi semplicemente riempire uno dei corpi, selezionare il corpo e usare quella funzione per estrarne automaticamente un metodo.

+0

Sono d'accordo, una riprogettazione sarebbe meglio. – Enyra

+1

Sono anche d'accordo: riprogettazione! Questo è un tipico caso in cui inizi a complicare le cose che sono semplici. BACIO. –

+0

Questo è un consiglio perfetto per la programmazione in uno stile imperativo (specialmente se ci sono solo due possibili funzioni), ma in realtà il passaggio della funzione consente un'emulazione abbastanza decente della funzione del linguaggio funzionale dell'applicazione parziale. –

1

Il tipo di una funzione viene scritto come Func < inParam1, inParam2, ..., returnParam>. Non sono sicuro se sono passati due piedi parametri "out" correttamente nei tipi "FUNC", ma si dovrebbe essere in grado di rendere la vostra funzione come

void TryGetLogon(Func<IGetClientUsername, string, out int?, bool> f) { 
    // ... 
    f(x, y, z, a); 
} 
// ... 
TryGetLogon(TryGetLogonUserIdByGraphicalUsername); 
+2

"out" e "ref" non sono legali negli argomenti di tipo. Dovrai definire un nuovo tipo di delegato. –

0

ne dite semplicemente passando un flag booleano?

private static bool TryGetLogonUserIdByUsername(
    IGetClientUsername clientController, 
    string sClientId, out int? logonUserId, bool graphical) 
{ 
    string username; 
    bool gotClient = false; 

    if (graphical) 
    { 
     gotClient = clientController.GetClientGraphicalUsername(
      sClientId, out username); 
    } 
    else 
    { 
     gotClient = clientController.GetClientUsername(
      sClientId, out username); 
    } 

    if (gotClient) 
    { 
       // ... snip common code ... 
    } 

    return false; 
} 
+0

Buona idea; anche se preferisco l'approccio delegato - meno codice per comunicare lo stesso intento –

+0

Quando viene data l'opzione, preferisco evitare i flag booleani come questo.Sposta la descrizione funzionale della funzione che stai chiamando dal nome della funzione alla lista dei parametri. (In questo caso, la fine dell'elenco dei parametri). Inoltre, il significato del booleano non è evidente nel callsite: devi cercare il nome del parametro per capire cosa significa. –

Problemi correlati