在可能未初始化的Dictionary元素上执行加号等于操作的简洁方法

我正在寻找一种扩展方法或任何其他建议,可以帮助我使这段代码尽可能简洁。

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(this Dictionary dict, K key, V value) { if( dict.ContainsKey(key) ) dict[key] += value; else dict[key] = value; } 

但是,当然,即使这样也无法编译,因为没有办法限制V的类型,使得运算符+=存在。

规则

  • 只有一次迭代通过double for循环。 在使用0值初始化字典之前,不允许循环一次。
  • 可以使用辅助方法或扩展方法,但我希望内循环是一个衬里。
  • 尽可能通用和可重用,这样我就不需要为不同类型(小数,整数等)的类似存储创建一堆相同的函数。

作为参考 – 在类的其他地方,键被定义为一个实际的元组(只有命名参数),这就是为什么它可以用作字典键:

 private Dictionary _EventGroupLayerLosses; public class EventGroupIDLayerTuple : Tuple { 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(this Dictionary dict, K key, V value, Func 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) Dogs, (x, y) => x.AddRange(y)); 

无尽的乐趣!

首先,我建议不要尽一切可能做一些尽可能短的可读性的潜在成本。 例如,我在foreach体周围添加括号,如果更可读的解决方案最终是两行而不是一行,我会对此感到高兴。

其次,我将假设对于您感兴趣的任何类型,默认值是自然零。

现在,你可以写:

 public static void AddOrSet(this Dictionary dict, K key, V value, Func addition) { V existing; dict.TryGetValue(key, out existing); dict[key] = addition(existing, value); } 

然后你可以使用:

 EventGroupLayerLosses.AddOrSet(new EventGroupIDLayerTuple(evt.EventGroupID, lyr), GetEL(evt.AsIfs, lyr.LimitInMillions, lyr.AttachmentInMillions), (x, y) => x + y); 

使用ConcurrentDictionary也可以很好地工作。

另外,如果可以,我会尝试将其作为LINQ查询重新编写。 如果GroupBySumToDictionary允许你以声明方式表达整个事情,我不会感到惊讶。

.NET 4有一种新类型的字典类, ConcurrentDictionary 。 这个类有非常有用的AddOrUpdate方法(带有一些重载),展示了你正在寻找的行为。

关于ConcurrentDictionary的MSDN文档

我们可以在这里使用空融合吗?

 foreach( Layer lyr in this.ProgramLayers ) foreach( UWBCEvent evt in this.BcEvents.IncludedEvents ) EventGroupLayerLosses[new EventGroupIDLayerTuple(evt.EventGroupID, lyr)] = (EventGroupLayerLosses[new EventGroupIDLayerTuple(evt.EventGroupID, lyr)] ?? 0) + GetEL(evt.AsIfs, lyr.LimitInMillions, lyr.AttachmentInMillions); 

你可以尝试这样的事情。

 private void AddOrUpdate(this Dictionary dict, K key, Func newValue, Func updateValue) { V value; if( !dict.TryGetValue(key, out value) ) value = newValue(); else value = updateValue(value); dict[key] = value; }