Is there an IDictionary implementation that, on missing key, returns the default value instead of throwing?
Indeed, that won't be efficient at all.
As per comments, in .Net Core 2+ / NetStandard 2.1+ / Net 5, MS added the extension method GetValueOrDefault()
For earlier versions you can write the extension method yourself:
public static TValue GetValueOrDefault<TKey,TValue>
(this IDictionary<TKey, TValue> dictionary, TKey key)
{
TValue ret;
// Ignore return value
dictionary.TryGetValue(key, out ret);
return ret;
}
Or with C# 7.1:
public static TValue GetValueOrDefault<TKey,TValue>
(this IDictionary<TKey, TValue> dictionary, TKey key) =>
dictionary.TryGetValue(key, out var ret) ? ret : default;
That uses:
- An expression-bodied method (C# 6)
- An out variable (C# 7.0)
- A default literal (C# 7.1)
Dictionary returning a default value if the key does not exist
TryGetValue
will already assign the default value for the type to the dictionary, so you can just use:
dictionary.TryGetValue(key, out value);
and just ignore the return value. However, that really will just return default(TValue)
, not some custom default value (nor, more usefully, the result of executing a delegate). There's nothing more powerful built into the framework. I would suggest two extension methods:
public static TValue GetValueOrDefault<TKey, TValue>(
this IDictionary<TKey, TValue> dictionary,
TKey key,
TValue defaultValue)
{
return dictionary.TryGetValue(key, out var value) ? value : defaultValue;
}
public static TValue GetValueOrDefault<TKey, TValue>(
this IDictionary<TKey, TValue> dictionary,
TKey key,
Func<TValue> defaultValueProvider)
{
return dictionary.TryGetValue(key, out var value) ? value : defaultValueProvider();
}
(You may want to put argument checking in, of course :)
Is there an IDictionary implementation that, on missing key, returns the default value instead of throwing?
Indeed, that won't be efficient at all.
As per comments, in .Net Core 2+ / NetStandard 2.1+ / Net 5, MS added the extension method GetValueOrDefault()
For earlier versions you can write the extension method yourself:
public static TValue GetValueOrDefault<TKey,TValue>
(this IDictionary<TKey, TValue> dictionary, TKey key)
{
TValue ret;
// Ignore return value
dictionary.TryGetValue(key, out ret);
return ret;
}
Or with C# 7.1:
public static TValue GetValueOrDefault<TKey,TValue>
(this IDictionary<TKey, TValue> dictionary, TKey key) =>
dictionary.TryGetValue(key, out var ret) ? ret : default;
That uses:
- An expression-bodied method (C# 6)
- An out variable (C# 7.0)
- A default literal (C# 7.1)
How can I change Dictionary so that it returns custom default value rather than throw exception if no such key?
You could make your own class which encapsulates a Dictionary<TKey,TValue>
, and implements IDictionary<TKey,TValue>
.
This will behave like a dictionary, but you can write the behavior to handle your non-existent key any way you wish.
However, you can't change the way the actual Dictionary<TKey,TValue>
class functions.
How to get null instead of the KeyNotFoundException accessing Dictionary value by key?
In the end I came up with a variant using a deriving from dictionary class with explicit interface implementation:
public interface INullValueDictionary<T, U>
where U : class
{
U this[T key] { get; }
}
public class NullValueDictionary<T, U> : Dictionary<T, U>, INullValueDictionary<T, U>
where U : class
{
U INullValueDictionary<T, U>.this[T key]
{
get
{
U val;
this.TryGetValue(key, out val);
return val;
}
}
}
So it exposes the functionality I need the following way:
//create some dictionary
NullValueDictionary<int, string> dict = new NullValueDictionary<int, string>
{
{1,"one"}
};
//have a reference to the interface
INullValueDictionary<int, string> idict = dict;
try
{
//this throws an exception, as the base class implementation is utilized
Console.WriteLine(dict[2] ?? "null");
}
catch { }
//this prints null, as the explicit interface implementation
//in the derived class is used
Console.WriteLine(idict[2] ?? "null");
Is there any sort of generic dictionary where attempting to get a non-existent key will return null?
The problem is that Dictionary<TKey, TValue>
allows you to store null
as a value, thus you wouldn't know if the key didn't exist, or just had a value of null
. The other problem is that null
is only valid for TValue
types that are reference types (or Nullable<T>
).
Now, that said, you could write an extension method yourself to do so:
public static class DictionaryUtilities
{
public static TValue SafeGet<TKey, TValue>(this Dictionary<TKey, TValue> dict, TKey key, TValue defaultIfNotFound = default(TValue))
{
TValue value;
return dict.TryGetValue(key, out value) ? value : defaultIfNotFound;
}
}
In this way, you could query your dictionary like this:
var sample = new Dictionary<int, string> { {1, "One"}, {2, "Two}, {3, "Three"} };
var willBeNull = sample.SafeGet(4);
var willBeHi = sample.SafeGet(4, "HI!");
Though, of course, the above code would return the default of value types if TValue
was a value type (that is, if the value type was int
, it would return 0
on not found, by default.)
Again, though, you could create two forms, one where TValue
is constrained to class
and one to struct
that would allow you to return a Nullable<T>
for value types...
ConcurrentDictionary cast to IDictionary, indexer for missing key no longer throws exception
From https://msdn.microsoft.com/en-us/library/system.collections.idictionary.item(v=vs.110).aspx :
The element with the specified key, or null if the key does not exist.
So this is expected behaviour.
This seems to be dangerous behavior: an explicit implementation should not vary so much from the regular public indexer, should it?
It darn well should when its documented as doing so, unless there's a very good reason not to (enough really for the documentation to be considered buggy).
So, if the only known type that I have is IDictionary, how can I check for the existence of a key short of looping through all the keys and comparing?
IDictionary.Contains
though this is a nuisance (and a race in multi-threaded scenarios), which is why KeyNotFoundException
and IDictionary<TKey, TValue>.TryGetValue
were introduced with 2.0.
The lack of a TryGetValue
method meant that in 1.1 the only way to have a test on an IDictionary
for a key that may not be present without throwing was to know that null
was never going to be added in there. This is far from ideal, so the generic equivalent changed that, but its too late to update IDictionary
to match as all existing uses would be broken.
Alternative of ?? for non nullable types (e.g. when looking up dictionary keys)
You can replace if checking with ternary operator
var value = someDictionary.TryGetValue(someKey, out var val) ? val : anotherValue;
a default value for a Dictionary in c#
Check if it exists, if not, return a default value:
Model.AnswserKeywordDictionary.ContainsKey("myKey")
? Model.AnswserKeywordDictionary["myKey"]
: "default"
You could also create an extension method for that:
public static Dictionary<K, V> GetValueOrDefault<K, V>(this Dictionary<K, V> dictionary, K key, V defaultValue)
{
V val;
if (dictionary.TryGetValue(key, out val))
{
return val ?? defaultValue;
}
return defaultValue;
}
Use it like this:
Model.AnswserKeywordDictionary.GetValueOrDefault("myKey", "default")
Is there an IDictionary implementation that keeps the keys in the order they were added ? (NOT sorted order)
Could you perhaps just keep a List
and a Dictionary
that allows you to lookup where keys are in the list? That would allow you to get the key/value pairs in order of addition but still maintain O(1)
lookup.
Related Topics
Is SQLcommand.Dispose() Required If Associated SQLconnection Will Be Disposed
Why Is Only the UI Thread Allowed to Modify the Ui
Passing Command-Line Arguments in C#
How to Get Around the "'" Problem in SQLite and C#
Practical Uses for the "Internal" Keyword in C#
How to Create a Custom Attribute in C#
What Are Best Practices for Using Smtpclient, Sendasync and Dispose Under .Net 4.0
Should I Always Return Ienumerable<T> Instead of Ilist<T>
How to Solve Operator '!=' Cannot Be Applied to Operands of Type 'T' and 'T'
How to Seed an Admin User in Ef Core 2.1.0
Does Lock() Guarantee Acquired in Order Requested
Need to Perform Wildcard (*,, etc) Search on a String Using Regex
How to Add Xmlinclude Attribute Dynamically
Using Isassignablefrom with 'Open' Generic Types
The Type '...' Has No Constructors Defined
Conditional Compilation Depending on the Framework Version in C#