在可能未初始化的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查询重新编写。 如果GroupBy
, Sum
和ToDictionary
允许你以声明方式表达整个事情,我不会感到惊讶。
.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; }
- 应该“或”使用.Net4 Hasflags:enum.HasFlag(AccessRights.Read | AccessRights.Write)
- IIS 7.x,添加启用HTTPS的站点:SiteCollection.Add(string,string,string,byte )重载
- Wcf – 在exception之前重试3次?
- 渲染控件会在.Net 4中生成安全性exception
- 是CancellationTokenSource.CancelAfter()是否泄漏?
- 使用内存映射文件的缺点
- 目标框架更改4.0到3.5打破EF模型。 错误111:参照约束错误
- 缺少.NET 4.0的引用程序集文件夹
- 在发布模式下构建时出现混合模式错误