DelayedCall class

Very easy asynchronous delayed function calls. Typesafe invocation of nearly any method through generic parameters. Delayed calls can be cancelled, pulled forward or repeated later.

The DelayedCall class lets you implement deferred actions without the need to care about the timer handling yourself. Therefore, many methods for controlling the invocation are available, like to prematurely stop the timer, reset or change the delay, repeat the delayed call with the same or a different timeout, or determining whether the timer is still waiting to trigger.

Actions can be started in the same thread as the timer was started, or asynchronously in another thread. The first option is required to abe able to use the DelayedCall class in GUI environments like Windows Forms or WPF.

Compatibility: .NET Version 2.0 or newer

Example

The following sample code shows how the DelayedCall class can be used:

// Variable to keep track of the instance.
// This is not required for normal operation.
private DelayedCall dc;

private void button1_Click(object sender, EventArgs e)
{
    // Change button text in 2 seconds
    dc = DelayedCall<string>.Start(SetButtonText, "I was clicked", 2000);
}

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

private void HurryUpButton_Click(object sender, EventArgs e)
{
    // Invoke SetButtonText now, if the timeout isn't elapsed yet
    if (dc != null) dc.Fire();

    // Prepare a new DelayedCall object for later use
    DelayedCall exitDelay = DelayedCall.Create(Application.Exit, 0);
    // Now start the timeout with 0.5 seconds
    exitDelay.Reset(500);
}

Download

DelayedCall.cs30.1 KiBSource code of the DelayedCall class

Previous versions

Changes

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:

2007May15
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()).

Licence and terms of use

Copying and distribution of this file, with or without modification, are permitted provided the copyright notice and this notice are preserved. This file is offered as-is, without any warranty. (GNU All-Permissive licence)

Statistic data

  • Created on 2006-03-15, updated on 2015-02-08.
  • Ca. 180 lines of code, estimated development costs: 180 - 720 €