このコードをできるだけ簡潔にするのに役立つ拡張メソッドまたはその他の提案を探しています。
foreach( Layer lyr in this.ProgramLayers )
foreach( UWBCEvent evt in this.BcEvents.IncludedEvents )
EventGroupLayerLosses[new EventGroupIDLayerTuple(evt.EventGroupID, lyr)] +=
GetEL(evt.AsIfs, lyr.LimitInMillions, lyr.AttachmentInMillions);
上記のコードにはかなり明確な目的があります。複合キーを使用して値をグループにまとめています。ただし、辞書は最初は空であり、+= 演算子はバケットを 0 から開始することを認識しないため、このコードは失敗します。
私が思いつくことができる最高のものはこれです:
public V AddOrSet<K, V>(this Dictionary<K, V> dict, K key, V value)
{
if( dict.ContainsKey(key) )
dict[key] += value;
else
dict[key] = value;
}
しかしもちろん、演算子が存在するように V の型を制限する方法がないため、それでもコンパイルされません+=
。
ルール
- double for ループを 1 回だけ反復します。ディクショナリを 0 値で初期化する前に 1 回ループすることはできません。
- ヘルパーメソッドや拡張メソッドも使えるが、内側のループはワンライナーにしたい。
- 可能な限り汎用的かつ再利用可能にして、異なる型 (10 進数、整数など) で同様のバケットを作成するために同一の関数を多数作成する必要がないようにします。
参考までに - クラスの他の場所では、キーは実際のタプル (名前付きパラメーターのみ) として定義されているため、辞書キーとして使用できます。
private Dictionary<EventGroupIDLayerTuple, Decimal> _EventGroupLayerLosses;
public class EventGroupIDLayerTuple : Tuple<Int32, Layer>
{
public EventGroupIDLayerTuple(Int32 EventGroupID, Layer Layer) : base(EventGroupID, Layer) { }
public Int32 EventGroupID { get { return this.Item1; } }
public Layer Layer { get { return this.Item2; } }
}
解決
Lambda 関数を 3 番目のパラメータとして拡張メソッドに渡すというアイデアをくれた Jon Skeet に感謝します。+= 操作に制限する必要さえなくなりました。値が既に存在する場合、新しい値を設定するために任意の操作を渡すことができるのは十分に一般的です。
//Sets dictionary value using the provided value. If a value already exists,
//uses the lambda function provided to compute the new value.
public static void UpdateOrSet<K, V>(this Dictionary<K, V> dict, K key, V value, Func<V, V, V> operation)
{
V currentValue;
if( dict.TryGetValue(key, out currentValue) )
dict[key] = operation(currentValue, value);
else
dict[key] = value;
}
例:
mySums.UpdateOrSet("Bucket1", 12, (x, y) => x + y);
myStrs.UpdateOrSet("Animals", "Dog", (x, y) => x + ", " + y);
myLists.UpdateOrSet("Animals", (List<T>) Dogs, (x, y) => x.AddRange(y));
無限の楽しみ!