我正在寻找一种扩展方法或任何其他可以帮助我使此代码尽可能简洁的建议。
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 的类型以使运算符+=
存在。
规则
- 只有一次迭代通过双 for 循环。之前不允许循环一次以使用 0 值初始化字典。
- 可以使用辅助方法或扩展方法,但我希望内循环是单线。
- 尽可能通用和可重用,这样我就不需要为具有不同类型(小数、整数等)的类似分桶创建一堆相同的函数。
供参考 - 在类的其他地方,键被定义为实际的元组(仅带有命名参数),这就是它可以用作字典键的原因:
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; } }
}
解决方案
感谢 Jon Skeet 提出将 Lambda 函数作为第三个参数传递给我的扩展方法的想法。甚至不再需要将其限制为 += 操作。如果值已经存在,则可以传递任何操作来设置新值,这是足够通用的。
//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));
无尽的乐趣!