キーが欠落している場合、ディクショナリへのインデクサーは例外をスローします。代わりに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;
それは以下を使用します:
これらの拡張メソッドを実行すると役立ちます。
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();
}
.net core 2以上(C#7.X)を使用している場合、 CollectionExtensions クラスが導入され、キーが存在しない場合に GetValueOrDefault メソッドを使用してデフォルト値を取得できます辞書。
Collections.Specialized.StringDictionary
は、欠落しているキーの値を検索するときに、例外以外の結果を提供します。また、デフォルトでは大文字と小文字が区別されません。
特殊な用途にのみ有効であり、ジェネリックよりも前に設計されているため、コレクション全体を確認する必要がある場合、非常に優れた列挙子はありません。
ASP.NET MVCを使用している場合、ジョブを実行するRouteValueDictionaryクラスを活用できます。
public object this[string key]
{
get
{
object obj;
this.TryGetValue(key, out obj);
return obj;
}
set
{
this._dictionary[key] = value;
}
}
辞書のキー検索機能のインターフェースを定義できます。私はおそらく次のようなものとして定義します:
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つは、 out variables 機能です。C#6の null-conditional operator を式に追加すると、コードはかなり大きくなります。追加の拡張メソッドを必要とせず、よりシンプルに。
var dic = new Dictionary<string, MyClass>();
dic.TryGetValue("Test", out var item);
item?.DoSomething();
この欠点は、このようにすべてをインラインで実行できないことです。
dic.TryGetValue("Test", out var item)?.DoSomething();
これを行う必要がある場合は、Jonのような1つの拡張メソッドをコーディングする必要があります。
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();
}
}
これは、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;
_
default(TV)
を返したい場合を最適化するために、2つの関数を用意する方が効率的です。
_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!)が必要です。
TryGetValue
を確認し、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
カプセル化を使用して、C++に精通しているユーザー向けにSTL mapと非常によく似た動作を持つ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
を使用してキーが存在するかどうかを確認し、通常取得された値または条件演算子を使用してデフォルト値を返すこの1行はどうでしょうか。
var myValue = myDictionary.ContainsKey(myKey) ? myDictionary[myKey] : myDefaultValue;
デフォルト値をサポートする新しいDictionaryクラスを実装する必要はありません。ルックアップステートメントを上記の短い行に置き換えるだけです。