2

I've found an example in .Net documentation on how to implement AsyncCache. They suggest the following:

public class AsyncCache<TKey, TValue>
{
    private readonly Func<TKey, Task<TValue>> _valueFactory;
    private readonly ConcurrentDictionary<TKey, Lazy<Task<TValue>>> _map;

    public AsyncCache(Func<TKey, Task<TValue>> valueFactory)
    {
        if (valueFactory == null) throw new ArgumentNullException("loader");
        _valueFactory = valueFactory;
        _map = new ConcurrentDictionary<TKey, Lazy<Task<TValue>>>();
    }

    public Task<TValue> this[TKey key]
    {
        get
        {
            if (key == null) throw new ArgumentNullException("key");
            return _map.GetOrAdd(key, toAdd => 
                new Lazy<Task<TValue>>(() => _valueFactory(toAdd))).Value;
        }
    }
}

Why can't we just use Task in _map dictionary without wrapping it in a Lazy? As for me, Task itself behaves like a Lazy from that point that it executes only once and then we can access the result by Result property or by awaiting it.

So, can anybody explain what is the purpose of using Lazy here? Thanks

Olha Shumeliuk
  • 720
  • 7
  • 14
  • 1
    The Lazy wrapper doesn't invoke the factory method until the value is requested for the first time. Without the Lazy wrapper all of the task's code would be invoked early, which has its disadvantages. – Dai Jul 22 '18 at 18:26
  • yes, but it invoked in the very same time when we add one to the dictionary – Olha Shumeliuk Jul 22 '18 at 18:28
  • 8
    `GetOrAdd` can execute passed delegate multiple times, thus it can create multiple `Lazy` instances, but you access `Value` only of one of them, thus only one `Task` will be created. So `Lazy` likely used as guardian to create only one `Task` per key. – user4003407 Jul 22 '18 at 18:54
  • 2
    @PetSerAl is almost certainly correct. That being said, I would suggest `LazyWithNoExceptionCaching` over `Lazy` if the `Func` might throw an exception - https://stackoverflow.com/a/42567351/34092 . – mjwills Jul 22 '18 at 22:04
  • @mjwills This would not help if function successfully return failed task. – user4003407 Jul 23 '18 at 03:14
  • 1
    @mjwills My point that it is common for async methods to not throw exception, but rather to return failed task instead. At least compiler generated `async` methods done this way. So, you have little to no gain by using custom `Lazy` class, while have more code to test and support. – user4003407 Jul 23 '18 at 05:00
  • That is true @PetSerAl. If the `Func` definitely won't throw an exception there is no value in doing so. – mjwills Jul 23 '18 at 05:17
  • Does this answer your question? [Purpose of Lazy on this MSDN Example](https://stackoverflow.com/questions/26478998/purpose-of-lazyt-on-this-msdn-example) – Kristoffer Jälén Mar 07 '23 at 12:30

0 Answers0