Monday, November 22, 2021

A tiny and very handy CachedValue class

In server-side programming it happens very often that getting a value takes a lot of time, but the result can be stored for some time. A classical answer to this is of course caching. The class below allows to wrap this in a very simple model.

using System;

namespace IBSoft.Helpers
{
    public class CachedValue<T>
    {
        private T        value;
        private double   ttlInSeconds;
        private Func<T>  getValue;
        private DateTime expireAt = DateTime.MinValue;

        public CachedValue(int ttlInSeconds, Func<T> getValue)
        {
            this.ttlInSeconds = ttlInSeconds;
            this.getValue = getValue;
        }

        public T Value
        {
            get
            {
                if (DateTime.UtcNow > this.expireAt)
                {
                    T oldValue = this.value;

                    this.value = getValue();

                    var now = DateTime.UtcNow;
                    this.expireAt = now.AddSeconds(ttlInSeconds);

                    if (!this.value.Equals(oldValue))
                        this.LastValueUpdate = now;
                }

                return this.value;
            }
        }

        /// <summary>
        /// set when the value changes
        /// </summary>
        public DateTime LastValueUpdate { get; private set; }

        /// <summary>
        /// invalidates the value and causes reload on the next call
        /// </summary>
        public void Invalidate()
        {
            this.expireAt = DateTime.MinValue;
        }

        /// <summary>
        /// add implicit type conversion to T so that it's possible to use CacheValue<T> without the need to do cv.Value
        /// </summary>
        /// <param name="cv"></param>
        /// <returns></returns>
        public static implicit operator T(CachedValue<T> cv)
        {
            return cv.Value;
        }
    }
}

No comments:

Post a Comment