2009-06-22 6 views
5

Mam dwie metody w C# 3.5, które są identyczne bar jeden wywołanie funkcji, w opisie poniżej, zobacz clientController.GetClientUsername vs clientController.GetClientGraphicalUsernameW języku C# 3.5, Jak przekazać, która metoda wywoływania obiektu jako parametr

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; 
    } 

Czy istnieje sposób (delegaci? lambda), że mogę przechodzić w którym metoda na clientController chcę zadzwonić?

Dzięki!

+0

Próbka jest instancją ogólnego problemu napotkanego podczas refakturowania bloków kodu, które są identyczne dla różnych wywołań metod. W szczególności wydaje mi się, że widzę je w testach. –

Odpowiedz

7

Pewnie. Wystarczy zdefiniować delegata tak:

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

a następnie przekazać go do funkcji i wywołać ją:

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; 
} 

Aby wywołać funkcję z delegatem, można to zrobić:

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

func.Invoke ... kiepski styl ... – leppie

+2

Co mówisz, to kiepski styl? Przekazujesz delegata? Właśnie odpowiadałem na pytanie. Wywołanie ".Invoke()" na delegata? Wolę tę notację, ponieważ daje jasno do zrozumienia, że ​​używasz delegata. –

+2

Jestem z Joshem G, wydaje się to dość eleganckim rozwiązaniem problemu i jeśli nazwa "func" jest dokładnie wyjaśniona, co się dzieje –

0

Możesz przekazać MethodInfo, który można podnieść statycznie. Zgadzam się jednak, że może być wymagane przeprojektowanie.

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

Delegat to o wiele lepszy sposób na zrobienie tego niż refleksja. –

9

Chociaż można przekazać delegata jako parametr, sugeruję wybrać inną trasę. Enkapsuluj treść instrukcji if, która zawiera wspólny kod w innej funkcji i wywołaj tę funkcję w obu funkcjach.

Visual Studio ma funkcję "Refactor -> Extract Method" w menu kontekstowym. Możesz po prostu wypełnić jedno z ciał, wybrać ciało i użyć tej funkcji, aby automatycznie wyodrębnić z niej metodę.

+0

Zgadzam się, przeprojektowanie byłoby lepsze. – Enyra

+1

Zgadzam się również: przeprojektowanie! To typowy przypadek, w którym zaczynasz komplikować rzeczy, które są proste. POCAŁUNEK. –

+0

To jest doskonała rada do programowania w stylu imperatywu (szczególnie, jeśli istnieją tylko dwie możliwe funkcje), ale fakt, że funkcja ta przekazuje, pozwala na całkiem przyzwoitą emulację funkcji funkcjonalno-językowej aplikacji częściowej. –

1

Typ funkcji zapisany jest jako Func < inParam1, inParam2, ..., returnParam>. Nie jestem pewien, czy od ręki „out” Parametry są przekazywane odpowiednio w rodzaju „FUNC”, ale powinieneś być w stanie dokonać funkcję jak

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

"out" i "ref" nie są legalne w argumentach typu. Musisz zdefiniować nowy typ uczestnika. –

0

Jak o po prostu przekazując flagi wartości logicznej?

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

Dobry pomysł; chociaż wolę podejście delegowane - mniej kodu do komunikowania się z tym samym intencją –

+0

Po wybraniu opcji wolę unikać takich boolowskich flag.Przesuwają funkcjonalny opis funkcji, którą wywołujesz, z nazwy funkcji na listę parametrów. (W tym przypadku koniec listy parametrów). Ponadto, znaczenie boolean nie jest widoczne w callsite - musisz sprawdzić nazwę parametru, aby dowiedzieć się, co to znaczy. –