Allow adding errors and warnings and remove both after a while with seperate timers I use this in my Blazor application, letting other services send errors or warnings to this AlertService. The service triggers an update of a component which simply displays the messages, errors in red warnings in yellow.
While getting no new warnings, the existing warnings should be deleted, every 5 seconds one less warning being displayed. Same goes for errors.
When a new warning comes in, the timer restarts (so when the service was 1 second before deleting a warning, that timer is now void and the new timer starts with 5 seconds again).
This is my class, I feel there must be a better way but I haven't figured it out yet:
    public class AlertService
{
    private readonly ObservableCollection&lt;string&gt; errors = new ObservableCollection&lt;string&gt;();
    private readonly ObservableCollection&lt;string&gt; warnings = new ObservableCollection&lt;string&gt;();
    private object? talkingStickErrors;
    private object? talkingStickWarnings;

    public ReadOnlyObservableCollection&lt;string&gt; Errors { get; }

    public ReadOnlyObservableCollection&lt;string&gt; Warnings { get; }

    public AlertService()
    {
        Errors = new ReadOnlyObservableCollection&lt;string&gt;(errors);
        Warnings = new ReadOnlyObservableCollection&lt;string&gt;(warnings);
    }

    public void Error(string message)
    {
        Console.WriteLine($&quot;Error: {message}&quot;);
        errors.Add(message);
        StartErrorTimer();
    }

    private void StartErrorTimer()
    {
        talkingStickErrors = new object();
        RemoveErrorAfterDelay(talkingStickErrors);
    }

    private async void RemoveErrorAfterDelay(object localTalkingStick)
    {
        while (true)
        {
            await Task.Delay(5000);
            if (localTalkingStick != talkingStickErrors || !errors.Any()) return;
            errors.RemoveAt(0);
        }
    }

    public void Warning(string message)
    {
        Console.WriteLine($&quot;Warning: {message}&quot;);
        warnings.Add(message);
        StartWarningTimer();
    }

    private void StartWarningTimer()
    {
        talkingStickWarnings = new object();
        RemoveWarningAfterDelay(talkingStickWarnings);
    }

    private async void RemoveWarningAfterDelay(object localTalkingStick)
    {
        while (true)
        {
            await Task.Delay(5000);
            if (localTalkingStick != talkingStickWarnings || !warnings.Any()) return;
            warnings.RemoveAt(0);
        }
    }
}

Also I couldn't think of a better word for the talking sticks. They're not exactly locks.
Update: No more talking sticks, it now just reused the last message it received.
Update 2: Talking sticks again, as string have no unique identity.
Update 3: Instead of an extra event, the alerts are now in ObservableCollections. I keep receiving stuff about encapsulation and Rx. ould appreciate someone helping me to DRY this.
Try this collection to hold your messages. It should do exactly what you are looking for - just create couple instances for errors and warnings.
public class MessageCollection : ObservableCollection&lt;string&gt;
{
    public MessageCollection()
    {
        Timer = new Timer(Callback, this, 5000, 5000);
        CollectionChanged += MessageCollection_CollectionChanged;
    }

    Timer Timer { get; }

    void MessageCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        Timer.Change(5000, 5000);
    }
    static void Callback(object state)
    {
        var that = (MessageCollection)state;
        if (that.Any())
        {
            that.CollectionChanged -= that.MessageCollection_CollectionChanged;
            that.RemoveAt(that.Count - 1);
            that.CollectionChanged += that.MessageCollection_CollectionChanged;
        }
    }
}