Category Archives: ViewModels

Adventures with DelayedAction (C#)

Today I worked on a project and found a piece of code, that left me wondering what the one who wrote it was actually thinking this should do. I know what the code is intended to do, but it was so weird, I wanted to share and try to find a better solution.

First of all the code, I didn’t copy & paste it, I wrote it from memory, my brain memory 🙂

Don’t copy and paste this

CancellationTokenSource _cancellationTokenSource = null;
        private async void SetText()
        {

            try
            {
                if (_cancellationTokenSource != null && !_cancellationTokenSource.IsCancellationRequested)
                {
                    _cancellationTokenSource.Cancel();
                }
                Interlocked.Exchange(ref _cancellationTokenSource, new CancellationTokenSource());
                await Task.Delay(2000, _cancellationTokenSource.Token);
                DoSearch();
            }
            catch
            {
            }
            finally
            {
                if (!_cancellationTokenSource.IsCancellationRequested)
                    _cancellationTokenSource.Cancel();
                _cancellationTokenSource.Dispose();
            }

        }

Any idea, what this is all about?
So let me just lift the curtain here. The function is being called within the Property-Setter, which is bound to an Entry. DoSearch() is just taking the Text and performing a full text search through a list of data. The challenge here is, that we don’t want to trigger the search on each keystroke, but when the user pauses for a second (or two in this case). Technically this code is producing a CancellationToken on each keystroke, waits 2 seconds, and cancels the previous delay task. TaskCanceledExceptions will be tolerated and ignored.

Try to make it better

If you have no clue, why this is bad code, you should just leave now. Really, go away. Let’s try to find a better solution. How about a counter? Let’s think of it like house, and every keystroke is a person going into that house. It’s a very special house, people are only visiting the house 2 seconds, then they leave. It’s not a brothel, maybe it stinks in the house, so the people leave right away. We stand at the port and count everyone that’s going and subtracting the peopel going out. After we reach to zero, we just close the door and go home, or whatever action we intent.

 public class DelayedAction
    {
        private Action _action;
        private int _callCounter = 0;
        private TimeSpan _delay = TimeSpan.FromSeconds(1);
        public DelayedAction(Action action)
        {
            _action = action;
        }

        public DelayedAction SetDelay(TimeSpan delay)
        {
            _delay = delay;
            return this;
        }

        public async void Trigger()
        {
            Interlocked.Increment(ref _callCounter);
            // now wait
            await Task.Delay(_delay);
            Interlocked.Decrement(ref _callCounter);
            if (_callCounter == 0)
            {
                _action();
            }

        }
    }

That’s it! Just a counter. No Exceptions, no CancellationTokens. We still have the Task.Delay but that’s way better than doing a SpinWait or BusyWait.

And here is how you can use it:

string _text;
DelayedAction _callSearchAction;
public MainPageViewModel(INavigationService navigationService)
    : base(navigationService)
{
    _callSearchAction = new DelayedAction(DoSearch)
                        .SetDelay(TimeSpan.FromMilliseconds(2000)); 
}


public string Text {
    get => _text;
    set
    {
        SetProperty(ref _text, value);
        //SetText();
        _callSearchAction.Trigger();
    }   
}

Do I really need to unsubscribe every event?

I’ve seen a lot of source code in my life. Some really good code, that I loved to read and understand. My wife loves to read good books, I love to read good source code. 🙂

But sometimes, you read lines of code and think: Why??? Why, is someone doing this??? I remember one code snipped, I’ve seen a couple of months ago. I think the developer was afraid of memory leaks, maybe he read an article about events causing memory leaks, when they are not detached.

To summon up the code, it was as simple as that:

A ViewModel_A published an event and ViewModel_B subscribed to it. The guy writing it (I think he tried to “optimize” it..) made both ViewModels IDisposable and ViewModel_B unsubscribed that event. It wasn’t quite clear who actually disposes the objects of ViewModel_B, but that’s another story.

But, do we really have to unsubscribe each event, we ever hook on? Short answer NO! It actually depends on your use case and the lifetime of each object. ViewModels usually have a short lifetime (as long as you are visiting the page), therefore you don’t normally have to destroy the subscription. (In regular settings, you actually don’t need events within ViewModels, but I will not say never)

So let’s have a look at our sample, with all available connections. We have a View, with ViewModel_A as the BindingContext. The View_A has a ListView with items, each item has ViewModel_B as the BindingContext. And each ViewModel subscribes to the UpdateEvent. (Ok, I made this one up, to be more logical, even though I would probably solve such a setting in a different way).

ModelConnections1

The Garbage Collectors looks through the references, and as long as View_A is somehow in a NavigationStack, or viewed on the screen, everything stays in memory. Let’s say our View_A gets disposed because the user closes the page (or whatever). Now we have lost all connections, and only the event will keep both ViewModels connected. But all of those ViewModels have no connection to a View anymore, so they are both dead, and the Garbage Collector will sooner or later clean them up.

But are there cases, when I need to tidy up on events?
Yes, definitely, there are some cases. You just have to keep one thing in mind:

The publisher keeps the subscriber alive. Not vice versa.

Let’s say we have a service, which is implemented as a singleton (or instantiated as such in an IOC container). So the object is alive over the whole app lifecycle. When this service publishes an event, and a ViewModel subscribes it, the ViewModel will never get destructed (perhaps sometime, when the app terminates). But beware, that just implementing an interface like IDisposable will not solve your problem .. you also need to use it and think of when you want to dispose of your object. Prism has a nice variant for PageViewModels; indestructible. When you use Prism, just have a look at it, it’ll help you with that decision.