Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to read the value from a Dictionary via .TryGetValue() if the value is a tuple?

Tags:

c#

I have a dictionary of type

Dictionary<int, (float, float)>

when trying to read the value from it I can't use this way

if (myDict.TryGetValue(1, out (float tupleItem1, float tupleItem2))) { /* ... */ }

because then I get compile errors

enter image description here

The way it works is

if (myDict.TryGetValue(1, out (float, float) theTuple)) { /* ... */ }

Is there a way I can directly initialize the variables like so?

if (!myDict.TryGetValue(1, out (float tupleItem1, float tupleItem2)))
{
    /* handle if not found */
    tupleItem1 = 111;
    tupleItem2 = -12345;
}
like image 951
Olaf Svenson Avatar asked Aug 02 '21 09:08

Olaf Svenson


People also ask

What does TryGetValue do?

Use the TryGetValue method if your code frequently attempts to access keys that are not in the dictionary. Using this method is more efficient than catching the KeyNotFoundException thrown by the Item[] property.

Is Dictionary TryGetValue thread safe?

Not only is it not thread safe; it will also throw with NullReferenceException if accessed while another thread is reorganizing the hash buckets. The lock statement is wicked fast, don't avoid it.

Can TryGetValue return null?

There appears to be a null value returned from the out parameter when using the trygetvalue method. The code appears to find the value in the cache if this is defined but a try get does not return a value and thus unable to be used.


2 Answers

You can't deconstruct directly in an out parameter yet unfortunately, see this proposal.

You'll have to deconstruct it yourself:

if (!myDict.TryGetValue(1, out var result))
{
    result = (111, -12345);
}

You can improve this situation slightly with an extension method:

public static class DictionaryExtensions
{
    public static TValue? TryGetValue<TKey, TValue>(this Dictionary<TKey, TValue> dict, TKey key) where TValue : struct
    {
        return dict.TryGetValue(key, out var result) ? result : null;
    }
}

This lets you write:

if (myDict.TryGetValue(1) is not (float tupleItem1, float tupleItem2))
{
    tupleItem1 = 111;
    tupleItem2 = -12345;
}
like image 91
canton7 Avatar answered Oct 17 '22 01:10

canton7


If you find yourself doing this a lot, you could write a simple little extension method to make it more readable:

public static class DictionaryExt
{
    public static TValue TryGetValueOrDefault<TKey, TValue>(this Dictionary<TKey, TValue> dict, TKey key, Func<TValue> getDefault)
    {
        return dict.TryGetValue(key, out var value) 
            ? value 
            : getDefault();
    }
}

Then your sample code could looks something like this:

var dict = new Dictionary<int, (float, float)>();

var result = dict.TryGetValueOrDefault(1, () => (111, -12345));

Console.WriteLine(result);

I chose to use Func<TValue> rather than TValue for the default so that you don't have to create a default value that isn't going to be used in the case that the dictionary already contains the key.

If you want a slightly simpler syntax and you don't care that the default is created for every call regardless of whether it's actually needed you could write it like this instead:

public static class DictionaryExt
{
    public static TValue TryGetValueOrDefault<TKey, TValue>(this Dictionary<TKey, TValue> dict, TKey key, TValue defaultValue)
    {
        return dict.TryGetValue(key, out var value)
            ? value
            : defaultValue;
    }
}

Which would be called like this:

var dict = new Dictionary<int, (float, float)>();

var result = dict.TryGetValueOrDefault(1, (111, -12345));

Console.WriteLine(result);

You could of course include both methods in DictionaryExt for more flexibility.

(It's also entirely possible that the overhead of creating a Func<TValue> for every call is greater than creating a tuple for each call, so if you're worried about performance you should test it. In either case, you could pass in a precreated Func or Tuple to avoid the creation overhead.)

like image 39
Matthew Watson Avatar answered Oct 17 '22 00:10

Matthew Watson