DelayedCall-Klasse

Sehr einfacher asynchroner verzögerter Aufruf von Funktionen. Unterstützt durch generische Parameter den typsicheren Aufruf nahezu jeder Methode. Aufrufe können abgebrochen, vorgezogen oder später wiederholt werden.

Mit der DelayedCall-Klasse lassen sich verzögerte Abläufe realisieren, ohne dass man sich selbst um das Handling mit einem Timer kümmern muss. Dafür stehen viele Methoden zur Steuerung des Aufrufs zur Verfügung, etwa um einen Timer vorzeitig zu stoppen, die Zeitverzögerung zurückzusetzen oder zu ändern, den verzögerten Aufruf mit derselben oder einer anderen Zeiteinstellung zu wiederholen oder abzufragen, ob noch auf den Aufruf gewartet wird.

Die Aktionen lassen sich im gleichen Thread starten, in dem der Timer gestartet wurde, oder asynchron in einem anderen Thread. Die erste Option ist notwendig, um die DelayedCall-Klasse in GUI-Umgebungen wie Windows Forms oder WPF verwenden zu können.

Kompatibilität: .NET Ab Version 2.0

Beispiel

Der folgende Beispielcode zeigt, wie die DelayedCall-Klasse verwendet werden kann:

// Variable, um eine Referenz der Instanz zu behalten.
// Dies ist für den normalen Betrieb nicht erforderlich.
private DelayedCall dc;

private void button1_Click(object sender, EventArgs e)
{
    // Schaltflächentext in 2 Sekunden ändern
    dc = DelayedCall<string>.Start(SetButtonText, "Ich wurde geklickt", 2000);
}

private void SetButtonText(string newText)
{
    button1.Text = newText;
}

private void HurryUpButton_Click(object sender, EventArgs e)
{
    // SetButtonText jetzt aufrufen, wenn die Zeit noch nicht abgelaufen ist
    if (dc != null) dc.Fire();

    // Neuen DelayedCall zur späteren Verwendung vorbereiten
    DelayedCall exitDelay = DelayedCall.Create(Application.Exit, 0);
    // Timeout jetzt mit 0,5 Sekunden starten
    exitDelay.Reset(500);
}

Download

DelayedCall.cs30,1 KiBQuelltext der DelayedCall-Klasse

Vorherige Versionen

Änderungen

2015Feb8
Dokumentation (XML-Kommentare) und Code-Stil komplett überarbeitet (mithilfe von StyleCop).
2015Jan28
Statische SupportsSynchronization-Eigenschaft hinzugefügt. Kompatibilitäts-Code aus 2007 in neueren Versionen entfernt; diese Version bleibt unverändert verfügbar.
2014Feb5
Vervollständigte XML-Kommentare zur Dokumentation.
2011Jul29
Code-Refactoring und Verbesserung der Abbruchbehandlung.
2009Sep18
Zuverlässigere Synchronisierung bei Cancel-Aufrufen durch Prüfung eines Abbruchflags unmittelbar vor dem Aufruf der Zielmethode.
2007Aug1
Die Synchronisation in den UI-Thread wird jetzt mit einem SynchronizationContext durchgeführt, anstelle eines System.Windows.Forms.Timer. Der Code wurde dadurch etwas kürzer und die Abhängigkeit zu System.Windows.Forms entfällt. Die einzige Änderung daraus ergibt sich nach meinen Tests für Nicht-UI-Anwendungen wie Konsolenanwendungen oder Worker-Threads: Während der Aufruf von Forms.Timer nie ausgeführt würde, kann das Problem jetzt erkannt werden. Ähnlich der CrossThreadCall-Prüfung eines Controls wird nun ein Ausnahmefehler erzeugt (InvalidOperationException) und die Verwendung der *Async-Methoden empfohlen, die in jedem Umfeld funktionieren.
2007Jul27
Die DelayedCall-Klasse wurde komplett neu geschrieben, um ein paar alte Unschönheiten zu beseitigen.
  • Die alte Aufrufmethode mit dem new-Operator wurde durch statische Methoden ersetzt, die jetzt gleich im Namen unterscheiden, ob man den Aufruf nur vorbereiten oder gleich starten möchte (Create, Start). Die alte Aufrufmethode ist noch als „veraltet“ zugelassen, sodass die Klasse kompatibel sein sollte.
  • Durch die Verwendung von generischen Unterklassen lassen sich Methoden mit bis zu 3 Parametern typsicher und ohne Wrapper-Funktion und Typkonvertierungen von object aufrufen. Nur Rückgabewerte dürfen sie nicht haben, die könnten aber ohnehin nicht verarbeitet werden. Eine Unterstützung für solche Funktionen ließe sich aber auch nachrüsten, ebenso wie die Unterstützung für mehr Parameter.
  • Methodenaufrufe sind jetzt auch asynchron, d. h. in einem ThreadPool-Thread möglich. Dafür gibt es zusätzliche Methoden (CreateAsync, StartAsync), die statt dem System.Windows.Forms.Timer einen System.Timers.Timer verwenden. Ansonsten wird die Methode auch weiterhin im Thread des Aufrufers gestartet. (Entgegen den Aussagen der MSDN haben nach meinen Messungen beide Klassen eine zeitliche Abweichung von max. ±10 ms.)
  • Alle DelayedCall-Instanzen werden in einer statischen Liste verwaltet, so lange sie laufen. Das sollte ein mögliches vorzeitiges Einsammeln durch den Garbage Collector verhindern, was mir bislang aber noch nie aufgefallen wäre. Man kann aber auch weiterhin selbst Referenzen auf eine Instanz halten, um sie später zu steuern.
  • Die Semantik der Fire-Methode wurde so geändert, dass ein doppelter Aufruf nahe dem geplanten Aufrufzeitpunkt verhindert wird. Um die Callback-Methode jederzeit sofort aufzurufen, sollte man Reset(0) verwenden.
  • Auch wenn es kein üblicher Anwendungsfall ist, sollten die statischen und Instanzmethoden der DelayedCall-Klassen threadsicher sein.

Die folgenden Einträge beziehen sich auf die alte Version der Datei:

2007Mai15
Es wurden neue Reset-Methoden hinzugefügt, um für Aufrufe mit einem Parameter gleichzeitig den Parameterwert zu ändern. Außerdem wurde der Ereignisaufruf im Fall eines Abbruchs besser gegen einen verzögerten Timer-Aufruf gesichert. Das Exception-Handling wurde auskommentiert, um Ausnahmefehler durch die Anwendung bearbeiten zu lassen.
2007Feb9
Die DelayedCall-Klasse enthält jetzt die Methode SetTimeout, mit der vorab die Verzögerungszeit gesetzt werden kann, ohne (wie beim ähnlichen Konstruktor sonst üblich) den Timer sofort zu starten.
2006Aug8
Die DelayedCall-Klasse unterstützt jetzt neue Konstruktoren, um einen Aufruf vorzubereiten, aber noch nicht zu starten und Reset-Methoden zum erneuten Starten des Timers. Dadurch kann man langlebige DelayedCall-Objekte in Programmen verwenden, die man immer wieder abbrechen und erneut starten kann.
2006Jul1
Die DelayedCall-Klasse unterstützt jetzt auch eine Abfrage, ob der Timer gerade läuft (IsWaiting), sowie die sofortige Ausführung der auszuführenden Funktion (Fire()).

Lizenz und Nutzungsbedingungen

Vervielfältigung und Weiterverbreitung dieser Datei, verändert oder unverändert, sind gestattet, vorausgesetzt die Urheberrechtsangabe und dieser Hinweis bleiben erhalten. Diese Datei wird wie vorliegend ohne jegliche Garantie oder Gewährleistung angeboten. (GNU All-Permissive-Lizenz)

Statistische Daten

  • Erstellt am 2006-03-15, aktualisiert am 2015-02-08.
  • Ca. 180 Codezeilen, geschätzte Ent­wick­lungs­kos­ten: 180 - 720 €