タイトルは十分に基本的ですが、なぜ私はできないのですか?
Dictionary<string, string> dic = new Dictionary<string, string>();
dic.AddRange(MethodThatReturnAnotherDic());
タイトルは十分に基本的ですが、なぜ私はできないのですか?
Dictionary<string, string> dic = new Dictionary<string, string>();
dic.AddRange(MethodThatReturnAnotherDic());
回答:
元の質問へのコメントはこれをかなりうまくまとめています:
誰もその機能を設計、指定、実装、テスト、文書化、出荷したことがないからです。-@Gabe Moothart
なぜですか?おそらく、辞書をマージする動作は、フレームワークのガイドラインに適合する方法で推論できないためです。
AddRange
データの範囲は重複したエントリを許容するため、範囲は連想コンテナにとって意味がないため、存在しません。たとえば、IEnumerable<KeyValuePair<K,T>>
そのコレクションがある場合、重複したエントリを防ぎません。
キーと値のペアのコレクションを追加したり、2つの辞書をマージしたりする動作は簡単です。ただし、複数の重複エントリを処理する方法の動作は異なります。
重複を処理するときのメソッドの動作は何ですか?
私が考えることができる少なくとも3つの解決策があります:
例外がスローされた場合、元の辞書の状態はどうなりますか?
Add
ほとんどの場合、アトミック操作として実装されます。成功してコレクションの状態を更新するか、失敗してコレクションの状態は変更されません。AddRange
エラーを複製するため、失敗する可能性が、方法は一貫してその動作を維持するためにAdd
も、重複に例外をスローすることによって、それは、原子作る、と変わらないように、元の辞書の状態を残すことであろう。
APIコンシューマーとして、重複する要素を繰り返し削除するのは面倒です。これはAddRange
、がすべての重複する値を含む単一の例外をスローする必要があることを意味します。
次に、選択は次のように要約されます。
両方のユースケースをサポートするための議論があります。そのためIgnoreDuplicates
に、署名にフラグを追加しますか?
IgnoreDuplicates
基本となる実装が重複チェック用コードのバイパスと同じように(trueに設定)フラグはまた、有意な速度を提供するであろう。
これで、AddRange
両方のケースをサポートできるようにするフラグがありますが、文書化されていない副作用があります(これは、Frameworkデザイナーが回避するために真剣に取り組んだものです)。
概要
重複の処理に関しては、明確で一貫した期待される動作がないため、それらをすべて一緒に処理せず、最初からメソッドを提供しない方が簡単です。
辞書をマージし続ける必要がある場合は、もちろん、独自の拡張メソッドを作成して辞書をマージすることができます。これは、アプリケーションで機能するように動作します。
AddMultiple
とは異なりますがAddRange
、実装が不安定になることはありません。すべての重複するキーの配列で例外をスローしますか?または、遭遇した最初の重複キーで例外をスローしますか?例外がスローされた場合、辞書の状態はどうなりますか?そのまま、または成功したすべてのキー?
Add
-それぞれをラップのいずれかAdd
でtry...catch
、重複道それをキャッチ。または、インデクサーを使用して、最初の値を新しい値で上書きします。または、を使用するContainsKey
前に使用を事前に確認しAdd
、元の値を保持します。フレームワークにAddRange
or AddMultiple
メソッドが含まれている場合、発生したことを伝えるための唯一の簡単な方法は例外を介したものであり、関連する処理と回復も複雑ではありません。
私はいくつかの解決策を持っています:
Dictionary<string, string> mainDic = new Dictionary<string, string>() {
{ "Key1", "Value1" },
{ "Key2", "Value2.1" },
};
Dictionary<string, string> additionalDic= new Dictionary<string, string>() {
{ "Key2", "Value2.2" },
{ "Key3", "Value3" },
};
mainDic.AddRangeOverride(additionalDic); // Overrides all existing keys
// or
mainDic.AddRangeNewOnly(additionalDic); // Adds new keys only
// or
mainDic.AddRange(additionalDic); // Throws an error if keys already exist
// or
if (!mainDic.ContainsKeys(additionalDic.Keys)) // Checks if keys don't exist
{
mainDic.AddRange(additionalDic);
}
...
namespace MyProject.Helper
{
public static class CollectionHelper
{
public static void AddRangeOverride<TKey, TValue>(this IDictionary<TKey, TValue> dic, IDictionary<TKey, TValue> dicToAdd)
{
dicToAdd.ForEach(x => dic[x.Key] = x.Value);
}
public static void AddRangeNewOnly<TKey, TValue>(this IDictionary<TKey, TValue> dic, IDictionary<TKey, TValue> dicToAdd)
{
dicToAdd.ForEach(x => { if (!dic.ContainsKey(x.Key)) dic.Add(x.Key, x.Value); });
}
public static void AddRange<TKey, TValue>(this IDictionary<TKey, TValue> dic, IDictionary<TKey, TValue> dicToAdd)
{
dicToAdd.ForEach(x => dic.Add(x.Key, x.Value));
}
public static bool ContainsKeys<TKey, TValue>(this IDictionary<TKey, TValue> dic, IEnumerable<TKey> keys)
{
bool result = false;
keys.ForEachOrBreak((x) => { result = dic.ContainsKey(x); return result; });
return result;
}
public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
{
foreach (var item in source)
action(item);
}
public static void ForEachOrBreak<T>(this IEnumerable<T> source, Func<T, bool> func)
{
foreach (var item in source)
{
bool result = func(item);
if (result) break;
}
}
}
}
楽しんで。
ToList()
辞書は必要ありませんIEnumerable<KeyValuePair<TKey,TValue>
。また、既存のキー値を追加すると、2番目と3番目のメソッドメソッドがスローされます。良い考えではありません、あなたは探していましたTryAdd
か?最後に、2つ目は次のように置き換えることができますWhere(pair->!dic.ContainsKey(pair.Key)...
ToList()
良い解決策ではないので、コードを変更しました。try { mainDic.AddRange(addDic); } catch { do something }
3番目の方法がわからない場合に使用できます。2番目の方法は完全に機能します。
私のように誰かがこの質問に遭遇した場合-IEnumerable拡張メソッドを使用して「AddRange」を達成することは可能です:
var combined =
dict1.Union(dict2)
.GroupBy(kvp => kvp.Key)
.Select(grp => grp.First())
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
辞書を組み合わせる際の主なトリックは、重複するキーを処理することです。上のコードでは、それが一部.Select(grp => grp.First())
です。この場合、重複のグループから最初の要素を取得するだけですが、必要に応じて、より高度なロジックを実装できます。
dict1
しない場合はどうなりますか?
var combined = dict1.Concat(dict2).GroupBy(kvp => kvp.Key, dict1.Comparer).ToDictionary(grp => grp.Key, grp=> grp.First(), dict1.Comparer);
何が起こったのかについてユーザーに適切な出力が不足していると思います。辞書では繰り返しキーを使用できないため、いくつかのキーが交差する2つの辞書をマージするにはどうすればよいでしょうか。もちろん、「気にしない」と言うこともできますが、これはfalseを返す、または繰り返しキーに対して例外をスローするという慣例を破っています。
Add
か。これは、同じスローしまうArgumentException
ことAdd
確実に、ありませんか?
NamedElementException
代わりに、またはArgumentExceptionのinnerExceptionとして、新しい例外タイプを作成できます(競合する名前付き要素を指定します)。 ...いくつかの異なるオプション、私は言うでしょう
あなたはこれを行うことができます
Dictionary<string, string> dic = new Dictionary<string, string>();
// dictionary other items already added.
MethodThatReturnAnotherDic(dic);
public void MethodThatReturnAnotherDic(Dictionary<string, string> dic)
{
dic.Add(.., ..);
}
または、addrangeや上記のパターンを使用するためにリストを使用します。
List<KeyValuePair<string, string>>
MethodThatReturnAnotherDic
。それはOPから来ています。質問と私の回答をもう一度確認してください。
新しいディクショナリを処理している場合(既存の行を失う必要がない場合)、別のオブジェクトリストからToDictionary()をいつでも使用できます。
したがって、あなたのケースでは、次のようなことをします:
Dictionary<string, string> dic = new Dictionary<string, string>();
dic = SomeList.ToDictionary(x => x.Attribute1, x => x.Attribute2);
Dictionary<string, string> dic = SomeList.ToDictionary...
キーが重複しないことがわかっている場合は、次のようにできます。
dic = dic.Union(MethodThatReturnAnotherDic()).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
キーと値のペアが重複している場合は、例外がスローされます。
これがフレームワークに含まれていない理由はわかりません。する必要があります。不確実性はありません。例外を投げるだけです。このコードの場合、例外をスローします。
var caseInsensitiveDictionary = new Dictionary<string, int>( StringComparer.OrdinalIgnoreCase);
ますか?
これは、c#7 ValueTuples(タプルリテラル)を使用した代替ソリューションです。
public static class DictionaryExtensions
{
public static Dictionary<TKey, TValue> AddRange<TKey, TValue>(this Dictionary<TKey, TValue> source, IEnumerable<ValueTuple<TKey, TValue>> kvps)
{
foreach (var kvp in kvps)
source.Add(kvp.Item1, kvp.Item2);
return source;
}
public static void AddTo<TKey, TValue>(this IEnumerable<ValueTuple<TKey, TValue>> source, Dictionary<TKey, TValue> target)
{
target.AddRange(source);
}
}
のように使用
segments
.Zip(values, (s, v) => (s.AsSpan().StartsWith("{") ? s.Trim('{', '}') : null, v))
.Where(zip => zip.Item1 != null)
.AddTo(queryParams);
他の人が述べたように、Dictionary<TKey,TVal>.AddRange
実装されていない理由は、重複しているケースを処理する方法がいくつかあるためです。これはまた、の場合であるCollection
か、などのインターフェースIDictionary<TKey,TVal>
、ICollection<T>
など
List<T>
それを実装するだけIList<T>
で、同じ理由でインターフェースが実装しないことに注意してください:期待される値の範囲をコレクションに追加するときに動作は、コンテキストによって大きく異なる可能性があります。
あなたの質問のコンテキストは、重複について心配していないことを示唆しています。
MethodThatReturnAnotherDic().ToList.ForEach(kvp => dic.Add(kvp.Key, kvp.Value));