Why does IMemoryCache.GetOrCreateAsync return "TItem?" (nullable) instead of "TItem"?
Asked Answered
C

2

15

The extension method GetOrCreateAsync for IMemoryCache:

public static async Task<TItem?> GetOrCreateAsync<TItem>(this IMemoryCache cache, object key, Func<ICacheEntry, Task<TItem>> factory)
{
    if (!cache.TryGetValue(key, out object? result))
    {
        using ICacheEntry entry = cache.CreateEntry(key);

        result = await factory(entry).ConfigureAwait(false);
        entry.Value = result;
    }

    return (TItem?)result;
}

Why do they returns TItem? instead of just TItem? Assuming my factory method never returns null, is it safe to assume it would never be null and just ignore it with null-forgiving operator !?

public async Task<Foo> GetFooAsync()
    => (await cache.GetOrCreateAsync("Foo", async _ => new Foo()))!
Cashier answered 12/1, 2023 at 8:22 Comment(6)
the reason it returns a nullable is because of the ? after the type. This symbolises a nullable variable. The reason it probably returns like a nullable is because result could be null, its not safe to assume that it won't be null. I suggest implementing a null check before using the return data.Cassey
@rbdeenk I believe they can just as well cast it to TItem instead of TItem?. But you are right that I want to know why they don't do that, there may be a situation that somehow it could be null. However that's extra logic for getting Foo incase it's null (basically almost copy the factory method)Cashier
GetOrCreate() is only a part of the whole interface. Maybe other methods allow writing cache values that are null?Dreadful
have a look at this post on the dotnet github github.com/dotnet/runtime/issues/77266Cassey
@GoodNightNerdPride yep you are right, per the Github comment. So if I do not write the cache myself with other method (or no null value) I can safely ignore it I guess.Cashier
@rbdeenk Thanks, that's the correct one. Somehow I missed that one when Googling.Cashier
C
10

This github post has some important factors as to why they chose to make it a nullable return type. Here are a couple things to consider.

It is possible to cache a null value according to eerhardt.

In the post Stephen Toub states the following:

One of our overarching principles for nullable annotations in the core libraries it that they should never lie and say null isn't a possible result when in fact it is. This does lead to some cases where a method is annotated as returning a T? even if null is rare or relegated to a corner-case, e.g. Activator.CreateInstance returns T? because for example it can actually be null if you specify a Nullable as the type, but it means the nullability of the return value can be trusted: if it says it's non-nullable, then it won't ever be null, and callers don't need to guard against dereferencing null. The principles are outlined in https://github.com/dotnet/runtime/blob/main/docs/coding-guidelines/api-guidelines/nullability.md.

I hope this gives a clear idea why they chose to make it nullable. In your case if you are 100% sure that you won't cache any nullables you could in theory ignore the fact that it can return a null value. Although I don't recommend it in any case you program grows/ changes and requires this to be possible.

Cassey answered 12/1, 2023 at 8:55 Comment(0)
F
0

You could get null back if something other than your Factory method cached a null value into IMemoryCache with that key. Hence why it's technically valid and possible.

Fixing answered 10/5 at 17:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.