Lazy <T> with Expiration
I want to implement the expiration time of a Lazy object. The expiration cooldown should begin with the first extraction of the value. If we get the value and the expiration time is passed, we will re-execute the function and reset the expiration time.
I am not familiar with extensions, partial keywords, and I do not know how to do this.
thanks
EDIT:
Code so far:
NEW EDIT:
new code:
public class LazyWithExpiration<T> { private volatile bool expired; private TimeSpan expirationTime; private Func<T> func; private Lazy<T> lazyObject; public LazyWithExpiration( Func<T> func, TimeSpan expirationTime ) { this.expirationTime = expirationTime; this.func = func; Reset(); } public void Reset() { lazyObject = new Lazy<T>( func ); expired = false; } public T Value { get { if ( expired ) Reset(); if ( !lazyObject.IsValueCreated ) { Task.Factory.StartNew( () => { Thread.Sleep( expirationTime ); expired = true; } ); } return lazyObject.Value; } } } I don’t think Lazy<T> will have any effect here, it is more like a general approach, essentially similar to a singleton pattern.
You will need a simple wrapper class that will either return the real object or pass all the calls to it.
I would try something like this (from memory, so it might include errors):
public class Timed<T> where T : new() { DateTime init; T obj; public Timed() { init = new DateTime(0); } public T get() { if (DateTime.Now - init > max_lifetime) { obj = new T(); init = DateTime.Now; } return obj; } } To use, you would simply use Timed<MyClass> obj = new Timed<MyClass>(); , not MyClass obj = new MyClass(); . And the actual calls will be obj.get().doSomething() instead of obj.doSomething() .
Edit:
Just note that you don’t have to combine an approach like mine above with Lazy<T> , because you are essentially forcing an initialization delay. Of course, you, of course, can determine the maximum lifetime in the constructor.
I agree with other commentators that you should probably not touch Lazy at all. Lazy is not very complicated if you ignore several thread protection options, so just implement it from scratch.
I really like the idea, by the way, although I don’t know if I will comfortably use it as a general-purpose caching strategy. This may be sufficient for some simpler scenarios.
Here is my blow to him. If you do not need to be thread safe, you can simply remove the lock. I don’t think you can use the double check lock pattern here because of the likelihood that the cached value might be invalid inside the lock.
public class Temporary<T> { private readonly Func<T> factory; private readonly TimeSpan lifetime; private readonly object valueLock = new object(); private T value; private bool hasValue; private DateTime creationTime; public Temporary(Func<T> factory, TimeSpan lifetime) { this.factory = factory; this.lifetime = lifetime; } public T Value { get { DateTime now = DateTime.Now; lock (this.valueLock) { if (this.hasValue) { if (this.creationTime.Add(this.lifetime) < now) { this.hasValue = false; } } if (!this.hasValue) { this.value = this.factory(); this.hasValue = true; // You can also use the existing "now" variable here. // It depends on when you want the cache time to start // counting from. this.creationTime = Datetime.Now; } return this.value; } } } } I need the same thing. But I would prefer an implementation without locked read operations when there is no write.
public class ExpiringLazy<T> { private readonly Func<T> factory; private readonly TimeSpan lifetime; private readonly ReaderWriterLockSlim locking = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion); private T value; private DateTime expiresOn = DateTime.MinValue; public ExpiringLazy(Func<T> factory, TimeSpan lifetime) { this.factory = factory; this.lifetime = lifetime; } public T Value { get { DateTime now = DateTime.UtcNow; locking.EnterUpgradeableReadLock(); try { if (expiresOn < now) { locking.EnterWriteLock(); try { if (expiresOn < now) { value = factory(); expiresOn = DateTime.UtcNow.Add(lifetime); } } finally { locking.ExitWriteLock(); } } return value; } finally { locking.ExitUpgradeableReadLock(); } } } }