K.D. K.D. - 19 days ago 5
C# Question

Why does this async lambda function invocation not compile in C#?

I seem to be missing something really basic in my understanding. I am trying to call an async function that will take a

Func<T>
passed in by the caller, in case if the async function doesn't find a value in the cache. I am really puzzled as to why my code won't compile.

My calling code is as follows

static void Main(string[] args)
{
Func<Task<string>> dataFetcher = async () =>
{
string myCacheValue = await new HttpClient().GetStringAsync("http://stackoverflow.com/");
return myCacheValue;
};

MyCache<string> cache = new MyCache<string>();

string mValue = cache.GetOrCreateAsync("myCacheKey", dataFetcher).Result;

}


MyCache is as follows

internal class MyCache<T> where T: class
{
private Cache localCache = null;

public MyCache()
{
localCache = new Cache();
}
public T GetOrCreate(string cacheKey, Func<T> doWork)
{
T cachedObject = null;
if (string.IsNullOrEmpty(cacheKey) || string.IsNullOrWhiteSpace(cacheKey))
return cachedObject;

cachedObject = localCache.Get(cacheKey) as T;

if (null == cachedObject)
{
cachedObject = doWork();
}

localCache.Add(cacheKey, cachedObject, null, DateTime.Now.AddMinutes(5), Cache.NoSlidingExpiration, CacheItemPriority.Normal, null);

return cachedObject;
}

public async Task<T> GetOrCreateAsync(string cacheKey, Func<T> doWork)
{
T cachedObject = null;

if (string.IsNullOrEmpty(cacheKey) || string.IsNullOrWhiteSpace(cacheKey))
return cachedObject;

try
{
cachedObject = await Task.Factory.StartNew<T>(doWork);
localCache.Add(cacheKey, cachedObject, null, DateTime.Now.AddMinutes(5), Cache.NoSlidingExpiration, CacheItemPriority.Normal, null);
}
catch (Exception)
{
cachedObject = null;
}
finally
{
}

return cachedObject;
}
}


In the calling code the line

string mValue = cache.GetOrCreateAsync("myCacheKey", dataFetcher).Result;


gives me a compile time error
Argument 2: cannot convert from System.Func<System.Threading.Tasks.Task<string>> to System.Func<string>


I am at a loss as to what am I missing out on? If someone could help that will be great!

Thanks
~KD

Answer

Use an overloaded method and pass a Func<Task<T>> argument to it. Then you can await on that result (which is a Task<T>).

public async Task<T> GetOrCreateAsync(string cacheKey, Func<Task<T>> doWorkAsync)
{
    T cachedObject = null;

    if (string.IsNullOrEmpty(cacheKey) || string.IsNullOrWhiteSpace(cacheKey))
        return cachedObject;

    try
    {
        cachedObject = await doWorkAsync();
        localCache.Add(cacheKey, cachedObject, null, DateTime.Now.AddMinutes(5), Cache.NoSlidingExpiration, CacheItemPriority.Normal, null);
    }
    catch (Exception)
    {
        cachedObject = null;
    }
    finally
    {
    }

    return cachedObject;
}
Comments