2009-06-30 16 views
6

Potrzebuję napisać funkcję delegata, która może "owinąć" niektóre podczas/spróbować/złapać kod wokół podstawowego połączenia UDP, aby zweryfikować link. Udało mi się pracować dla Func dla funkcji, która nie ma argumentów, ale nie mogę sprawić, żeby działała na działanie, która ma argument (ale nie ma powrotu). Nie wydaje mi się, aby przekazać argument w logiczny sposób bez narzekania kompilatora.Używanie akcji jako argument w języku C# (naśladowanie wskaźnika funkcji)

Czy to wszystko źle? Jestem nowy w C# i zasadniczo próbuję naśladować ideę wskaźnika funkcji. Czy nie powinienem przeciążać tej funkcji? Wiem, że nie można przeciążać delegatów (zakładam, że dlatego Func i Action istnieją).

to działa:

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

Ale tego nie robi:

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

Wywołanie konwencji (na taki, który działa):

udpCommand<uint>(someUdpCommand); 
+2

Dodatek - w rzeczywistości nie potrzebujesz T; możesz po prostu wziąć "Akcja" (brak argumentu) i wywołać jako: udpCommand (() => SomeMethod (123)); –

+0

Czy pozwoliłoby to mieć zmienną liczbę parametrów wejściowych do udpCommand bez przeciążania go, powiedzmy, do 4 parametrów? Właśnie to muszę zrobić. Czy nadal potrzebuję jednego Func i jednej akcji? – cgyDeveloper

Odpowiedz

10

Jeśli chcesz to być wystarczająco rodzajowy obsługiwać dowolną liczbę argumentów spróbuj przy użyciu non-genernic działania delegat:

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

w C# 3.0, można nazwać tak:

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

W języku C# 2.0 to trochę brzydsze:

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

To daje wykonanie odroczony bez blokowania cię do konkretnego podpisu metody.

EDIT

Właśnie zauważyć Jakbym ukradł komentarz Marc Gravell za ... przeprosiny Marca. Aby odpowiedzieć jak można zmniejszyć powielania, można mieć metoda Action wywołać metodę Func<T> coś takiego:

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

wierzę (a może się mylę), który wraca 0 nie jest bardziej kosztowne niż (domyślnie) powracający nieważny, ale mogę być daleko stąd.Nawet jeśli ma to jakiś koszt, wystarczy umieścić na stosie niewielki, mały, bitowy snoodge. W większości przypadków dodatkowe koszty nigdy nie wywołają żadnego smutku.

+0

Uwielbiam to miejsce. Właśnie to, czego potrzebowałem jako debiutant C#/VS.NET. Dziękuję. – cgyDeveloper

2

Chyba wystarczy wyjąć (wartość T) po "komendzie".

+0

Kompilator uskarża się z Hmmm T. – cgyDeveloper

+0

lub bez niego, ponieważ pytanie to było trochę niejednoznaczne, ale powinienem był sobie uświadomić, że próbujesz przekazać argument do Akcji. Zgadzam się z rozwiązaniem dostarczonym w zaakceptowanej odpowiedzi, która po prostu przekazuje również wartość T (chociaż nie w nawiasach). Gdybyś miał "wartość" dostępną w bloku kodu, to myślę, że działałby dobrze. –

+0

Ach, tak, wierzę, że to też zadziała. Dzięki za opinie. – cgyDeveloper

4

Czy chodziło Ci o:

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

Z numerem:

udpCommand(someUdpCommand, arg); 

Zauważ, że to może działać lepiej na C# 3.0, który ma silniejsze niż wnioskowanie typu rodzajowego C# 2.0.

+0

Aha! Ten wydaje się działać ... przynajmniej kompilator już nie narzeka. Przykłady Microsoftu tak naprawdę nie obejmują mojego przypadku użycia. Na szczęście używam C# 3.0. Przebieg może się różnić w C# 2.0. – cgyDeveloper

0

starasz się to zrobić ...

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

Wtedy będzie pracować tak ...

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

Action<int> fp = ActionWithInt; 

udpCommand<int>(fp, 10); // or whatever. 
Powiązane problemy