Jak dodać delegata do interfejsu C #

91

Muszę mieć kilku delegatów w mojej klasie.

Chciałbym użyć interfejsu do „przypomnienia” mi o ustawieniu tych delegatów.

Jak?

Moja klasa wygląda tak:

public class ClsPictures : myInterface
{
    // Implementing the IProcess interface
    public event UpdateStatusEventHandler UpdateStatusText;
    public delegate void UpdateStatusEventHandler(string Status);

    public event StartedEventHandler Started;
    public delegate void StartedEventHandler();
}

Potrzebuję interfejsu, aby wymusić na tych delegatach:

public interface myInterface
{
   // ?????
}
Asaf
źródło

Odpowiedzi:

142

To są deklaracje typów delegatów . Nie należą do interfejsu. Jednak zdarzenia korzystające z tych typów delegatów mogą znajdować się w interfejsie:

public delegate void UpdateStatusEventHandler(string status);
public delegate void StartedEventHandler();

public interface IMyInterface
{       
    event UpdateStatusEventHandler StatusUpdated;    
    event StartedEventHandler Started;
}

Implementacja nie będzie (i nie powinna) ponownie zadeklarować typu delegata, nie bardziej niż ponownie zadeklarowałaby jakikolwiek inny typ używany w interfejsie.

Jon Skeet
źródło
To po prostu nie działa dla mnie. Jestem pewien, że zrobiłem wszystko w ten sposób, a mimo to mój program obsługi w interfejsie nie jest rozpoznawany. Moja klasa, która implementuje interfejs, narzeka, że ​​nie pasuje do zwracanego typu interfejsu System.EventHandler.
Chucky,
1
@Chucky: Wygląda na to, że powinieneś zadać nowe pytanie z krótkim, ale kompletnym przykładem demonstrującym problem. Odpowiedź Dałem naprawdę robi pracę.
Jon Skeet,
4
Nie zdawałem sobie sprawy, że delegaci mają taki sam poziom dostępności jak klasy. Zawsze myślałem, że trzeba je zamknąć w klasie.
Purusartha
7
@Purusartha: To nie jest dostępność - to tylko kwestia tego, czy delegaci są typami (w rzeczywistości klasami), tak samo jak interfejsami itp.
Jon Skeet,
29

Od .NET 3.5 można również używać delegatów System.Action, bez konieczności deklarowania własnego typu.

Spowodowałoby to następujący interfejs:

public interface myInterface
{       
   // Implementing the IProcess interface
   event Action<String> UpdateStatusText;

   event Action Started;
}
DELUXEnized
źródło
+1. Uważam, że Action / Func jest znacznie bardziej eleganckie (i czytelne) niż tradycyjne typy delegatów i możesz je zdefiniować w swoim interfejsie.
chrnola,
2
Ta odpowiedź nie odpowiada na pytanie (jak zaimplementować myInterface).
lharper71
10

Po prostu ujawnij delegata jako właściwość

public delegate void UpdateStatusEventHandler(string status);
public delegate void StartedEventHandler();

public interface IMyInterface
{       
    UpdateStatusEventHandler StatusUpdated {get; set;}    
    StartedEventHandler Started {get; set;}
}
Cornet Gregory
źródło
7

Odpowiedź Jona Skeeta jest prawidłowa, chcę tylko dodać notatkę.

Interfejsy nie służą do „przypominania” o tym, co należy zrobić lub co należy uwzględnić na zajęciach. Interfejsy są środkami abstrakcji używanymi w programowaniu obiektowym i metodach projektowania. Może w ogóle nie potrzebujesz deklaracji interfejsu, chyba że chcesz zobaczyć kilka konkretnych instancji klas jako interfejs w innym miejscu w programie (abstrakcja).

Jeśli chcesz wymusić pewne standardy kodowania w swoim projekcie, możesz spróbować użyć narzędzi do analizy kodu (takich jak Visual Studio) - zezwalają na rozszerzenia, które możesz włączyć, aby dodać własne reguły analizy kodu.

Używając analizy kodu, jeśli „zapomnisz” o dodaniu delegatów (chociaż nie widzę sensu zapominania o tym, tak jakby delegat nie był używany, nie jest potrzebny), otrzymasz ostrzeżenie / błąd.

Iravanchi
źródło
1
Możesz mieć rację, ale są chwile, kiedy zakładasz, że nawet przy dobrej dokumentacji trudno o tym pamiętać po jakimś czasie. Próbuję ponownie wykorzystać swój kod, ale czasami nie pamiętam, co to jest rdzeń, a co nie (jeśli chodzi o delegatów - ciężko mi zobaczyć szerszy obraz ...)
Asaf
1

Jeden z komentarzy odwoływał się do zwracanego typu programu obsługi zdarzeń. Czy bardziej interesuje Cię typ programu obsługi lub dane pochodzące ze zdarzenia? Jeśli to drugie, może to pomóc. Jeśli nie, to rozwiązanie nie wystarczy, ale może pomóc Ci zbliżyć się do tego, czego szukasz.

Wszystko, co musisz zrobić, to zadeklarować swoje programy obsługi zdarzeń jako ogólne programy obsługi zdarzeń zarówno w interfejsie, jak i w implementacji, i możesz dostosować zwracane wyniki.

Twoja klasa betonu wyglądałaby tak:

public class ClsPictures : myInterface
{
    // Implementing the IProcess interface
    public event EventHandler<UpdateStatusEventArgs> UpdateStatusText;
    //no need for this anymore: public delegate void UpdateStatusEventHandler(string Status);

    public event EventHandler<StartedEventArgs> Started;
    //no need for this anymore: public delegate void StartedEventHandler();
}

Twój interfejs wyglądałby tak:

public interface myInterface
{
   event EventHandler<StartedEventArgs> Started;
   event EventHandler<UpdateStatusEventArgs> UpdateStatusText;
}

Teraz, gdy argumenty zdarzeń zwracają Twoje typy, możesz podłączyć je do dowolnego zdefiniowanego przez siebie modułu obsługi.

Dla odniesienia: https://msdn.microsoft.com/en-us/library/edzehd2t(v=vs.110).aspx

Reuben Soto
źródło
0

Interfejs odziedziczony w ramach Twojej klasy pochodnej przypomni Ci o zdefiniowaniu i powiązaniu rzeczy, które w nim zadeklarowałeś.

Ale możesz również chcieć użyć go jawnie i nadal będziesz musiał powiązać go z obiektem.

Na przykład używając wzorca Inversion of Control:

class Form1 : Form, IForm {
   public Form1() {
     Controls.Add(new Foo(this));
   }

   // Required to be defined here.
   void IForm.Button_OnClick(object sender, EventArgs e) {
     ...
     // Cast qualifier expression to 'IForm' assuming you added a property for StatusBar.
     //((IForm) this).StatusBar.Text = $"Button clicked: ({e.RowIndex}, {e.SubItem}, {e.Model})";
   }
 }

Możesz spróbować czegoś takiego.

interface IForm {
  void Button_OnClick(object sender, EventArgs e);
}


class Foo : UserControl {
  private Button btn = new Button();

  public Foo(IForm ctx) {
     btn.Name = "MyButton";
     btn.ButtonClick += ctx.Button_OnClick;
     Controls.Add(btn);
  }
}
Czas oczekiwania
źródło