辞書へのインデクサーは、キーがない場合に例外をスローします。代わりにdefault(T)を返すIDictionaryの実装はありますか?
「TryGetValue」メソッドについては知っていますが、それをlinqで使用することは不可能です。
これは私が必要とすることを効率的に行いますか?:
myDict.FirstOrDefault(a => a.Key == someKeyKalue);
ハッシュルックアップを使用する代わりにキーを反復するのではないかと思います。
辞書へのインデクサーは、キーがない場合に例外をスローします。代わりにdefault(T)を返すIDictionaryの実装はありますか?
「TryGetValue」メソッドについては知っていますが、それをlinqで使用することは不可能です。
これは私が必要とすることを効率的に行いますか?:
myDict.FirstOrDefault(a => a.Key == someKeyKalue);
ハッシュルックアップを使用する代わりにキーを反復するのではないかと思います。
回答:
実際、それはまったく効率的ではありません。
あなたは常に拡張メソッドを書くことができます:
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;
}
またはC#7.1の場合:
public static TValue GetValueOrDefault<TKey,TValue>
(this IDictionary<TKey, TValue> dictionary, TKey key) =>
dictionary.TryGetValue(key, out var ret) ? ret : default;
それは使用します:
return (dictionary.ContainsKey(key)) ? dictionary[key] : default(TValue);
System.Collections.Generic.CollectionExtensions
に十分であると判断しました。
これらの拡張メソッドを実行すると役立ちます。
public static V GetValueOrDefault<K, V>(this IDictionary<K, V> dict, K key)
{
return dict.GetValueOrDefault(key, default(V));
}
public static V GetValueOrDefault<K, V>(this IDictionary<K, V> dict, K key, V defVal)
{
return dict.GetValueOrDefault(key, () => defVal);
}
public static V GetValueOrDefault<K, V>(this IDictionary<K, V> dict, K key, Func<V> defValSelector)
{
V value;
return dict.TryGetValue(key, out value) ? value : defValSelector();
}
Func<K, V>
。もちろん、セレクターを作成できます。
誰かが.netコア2以上(C#7.X)を使用している場合、CollectionExtensionsクラスが導入され、キーが辞書にない場合にGetValueOrDefaultメソッドを使用してデフォルト値を取得できます。
Dictionary<string, string> colorData = new Dictionary<string, string>();
string color = colorData.GetValueOrDefault("colorId", string.Empty);
.Net Coreを使用している場合は、CollectionExtensions.GetValueOrDefaultメソッドを使用できます。これは、受け入れられた回答で提供されている実装と同じです。
public static TValue GetValueOrDefault<TKey,TValue> (
this System.Collections.Generic.IReadOnlyDictionary<TKey,TValue> dictionary,
TKey key);
public class DefaultIndexerDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
private IDictionary<TKey, TValue> _dict = new Dictionary<TKey, TValue>();
public TValue this[TKey key]
{
get
{
TValue val;
if (!TryGetValue(key, out val))
return default(TValue);
return val;
}
set { _dict[key] = value; }
}
public ICollection<TKey> Keys => _dict.Keys;
public ICollection<TValue> Values => _dict.Values;
public int Count => _dict.Count;
public bool IsReadOnly => _dict.IsReadOnly;
public void Add(TKey key, TValue value)
{
_dict.Add(key, value);
}
public void Add(KeyValuePair<TKey, TValue> item)
{
_dict.Add(item);
}
public void Clear()
{
_dict.Clear();
}
public bool Contains(KeyValuePair<TKey, TValue> item)
{
return _dict.Contains(item);
}
public bool ContainsKey(TKey key)
{
return _dict.ContainsKey(key);
}
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
_dict.CopyTo(array, arrayIndex);
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return _dict.GetEnumerator();
}
public bool Remove(TKey key)
{
return _dict.Remove(key);
}
public bool Remove(KeyValuePair<TKey, TValue> item)
{
return _dict.Remove(item);
}
public bool TryGetValue(TKey key, out TValue value)
{
return _dict.TryGetValue(key, out value);
}
IEnumerator IEnumerable.GetEnumerator()
{
return _dict.GetEnumerator();
}
}
辞書のキー検索機能のインターフェースを定義できます。おそらく次のように定義します。
Interface IKeyLookup(Of Out TValue)
Function Contains(Key As Object)
Function GetValueIfExists(Key As Object) As TValue
Function GetValueIfExists(Key As Object, ByRef Succeeded As Boolean) As TValue
End Interface
Interface IKeyLookup(Of In TKey, Out TValue)
Inherits IKeyLookup(Of Out TValue)
Function Contains(Key As TKey)
Function GetValue(Key As TKey) As TValue
Function GetValueIfExists(Key As TKey) As TValue
Function GetValueIfExists(Key As TKey, ByRef Succeeded As Boolean) As TValue
End Interface
非ジェネリックキーを使用するバージョンでは、非構造キータイプを使用するコードを使用していたコードで、ジェネリックタイプパラメーターでは不可能な任意のキー分散を可能にすることができます。一つは、可変の使用を許可すべきではないDictionary(Of Cat, String)
可変としてDictionary(Of Animal, String)
、後者ができるようになるので、SomeDictionaryOfCat.Add(FionaTheFish, "Fiona")
。しかし、そこの可変を使用してと間違って何もDictionary(Of Cat, String)
不変としてDictionary(Of Animal, String)
、以来、SomeDictionaryOfCat.Contains(FionaTheFish)
完全に整形式(それは返すべきであると考えるべきであるfalse
タイプでないもののために、辞書を検索しなくて、Cat
)。
残念ながら、そのようなインターフェースを実際に使用できる唯一の方法はDictionary
、インターフェースを実装するクラスでオブジェクトをラップする場合です。ただし、実行していることに応じて、そのようなインターフェースとそれが許可する差異により、努力する価値があります。
この質問は、TryGetValue
演劇がFirstOrDefault
ここで役割を果たすた。
注目したいのは、C#7の興味深い機能の1つに変数出力機能があります。C#6 のnull条件演算子を方程式に追加すると、追加の拡張メソッドを必要とせずに、コードをはるかに簡単にすることができます。
var dic = new Dictionary<string, MyClass>();
dic.TryGetValue("Test", out var item);
item?.DoSomething();
これの欠点は、このようにすべてをインラインで実行できないことです。
dic.TryGetValue("Test", out var item)?.DoSomething();
これを行う必要がある場合は、Jonのような1つの拡張メソッドをコーディングする必要があります。
以下は、C#7.1の世界向けの@JonSkeetのバージョンで、オプションのデフォルトを渡すこともできます。
public static TV GetValueOrDefault<TK, TV>(this IDictionary<TK, TV> dict, TK key, TV defaultValue = default) => dict.TryGetValue(key, out TV value) ? value : defaultValue;
戻りたい場合を最適化するには、2つの関数を使用する方が効率的ですdefault(TV)
。
public static TV GetValueOrDefault<TK, TV>(this IDictionary<TK, TV> dict, TK key, TV defaultValue) => dict.TryGetValue(key, out TV value) ? value : defaultValue;
public static TV GetValueOrDefault2<TK, TV>(this IDictionary<TK, TV> dict, TK key) {
dict.TryGetValue(key, out TV value);
return value;
}
残念ながら、C#にはコンマ演算子(またはC#6で提案されているセミコロン演算子)が(まだ?)ないので、オーバーロードの1つに実際の関数本体(gasp!)が必要です。
カプセル化を使用して、c ++に精通している方のために、STLマップに非常に似た動作のIDictionaryを作成しました。そうでない人のために:
TL / DR-SafeDictionaryは、コンピューターがメモリ不足(または火事)であるなど、悪意のあるシナリオ以外の状況では例外をスローしないように記述されています。これは、AddをAddOrUpdate動作に置き換え、インデクサーからNotFoundExceptionをスローする代わりにデフォルトを返すことによって行われます。
これがコードです:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
public class SafeDictionary<TK, TD>: IDictionary<TK, TD> {
Dictionary<TK, TD> _underlying = new Dictionary<TK, TD>();
public ICollection<TK> Keys => _underlying.Keys;
public ICollection<TD> Values => _underlying.Values;
public int Count => _underlying.Count;
public bool IsReadOnly => false;
public TD this[TK index] {
get {
TD data;
if (_underlying.TryGetValue(index, out data)) {
return data;
}
_underlying[index] = default(TD);
return default(TD);
}
set {
_underlying[index] = value;
}
}
public void CopyTo(KeyValuePair<TK, TD>[] array, int arrayIndex) {
Array.Copy(_underlying.ToArray(), 0, array, arrayIndex,
Math.Min(array.Length - arrayIndex, _underlying.Count));
}
public void Add(TK key, TD value) {
_underlying[key] = value;
}
public void Add(KeyValuePair<TK, TD> item) {
_underlying[item.Key] = item.Value;
}
public void Clear() {
_underlying.Clear();
}
public bool Contains(KeyValuePair<TK, TD> item) {
return _underlying.Contains(item);
}
public bool ContainsKey(TK key) {
return _underlying.ContainsKey(key);
}
public IEnumerator<KeyValuePair<TK, TD>> GetEnumerator() {
return _underlying.GetEnumerator();
}
public bool Remove(TK key) {
return _underlying.Remove(key);
}
public bool Remove(KeyValuePair<TK, TD> item) {
return _underlying.Remove(item.Key);
}
public bool TryGetValue(TK key, out TD value) {
return _underlying.TryGetValue(key, out value);
}
IEnumerator IEnumerable.GetEnumerator() {
return _underlying.GetEnumerator();
}
}
を使用してキーが存在するかどうかを確認しContainsKey
、通常は取得した値または条件演算子を使用してデフォルト値を返すこのワンライナーはどうですか?
var myValue = myDictionary.ContainsKey(myKey) ? myDictionary[myKey] : myDefaultValue;
デフォルト値をサポートする新しいディクショナリクラスを実装する必要はありません。ルックアップステートメントを上記の短い行に置き換えるだけです。
であるTryGetValue
場合、デフォルト値をチェックして返すのは1行の場合がありfalse
ます。
Dictionary<string, int> myDic = new Dictionary<string, int>() { { "One", 1 }, { "Four", 4} };
string myKey = "One"
int value = myDic.TryGetValue(myKey, out value) ? value : 100;
myKey = "One"
=> value = 1
myKey = "two"
=> value = 100
myKey = "Four"
=> value = 4
いいえ。それ以外の場合、キーは存在するがnull値が格納されている場合、どのようにして違いを知ることができるでしょうか。それは重要かもしれません。