简明的方式做一个加等于可能不被初始化字典元素上的操作做一个、不被、简明、初始化

2023-09-07 16:10:58 作者:恐高的鸟

我在找一个扩展方法或其他任何建议,可以帮助我使这个code尽可能简明扼要。​​

I'm looking for an extension method or any other suggestion that can help me make this code as concise as possible.

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);

以上code有一个相当明确的目的,我在瓢泼大雨值代入组,复合键。然而,这code将失败,因为字典最初是空的,+ =运算符将不知道0开始斗了。

The above code has a fairly clear purpose, I'm bucketing values into groups with a compound key. However, this code will fail because the dictionary is initially empty and the += operator won't know to start the bucket off at 0.

我能想出的最好的是这样的:

The best I can come up with is this:

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型使得运营商 + = 的存在。

规则

只有一个迭代通过双循环。通过前一次初始化字典0值,不允许循环。 辅助方法或扩展方法都可以使用,但我想内环成为一个衬里。 要尽可能通用的,可重复使用的可能,这样我就不需要创建一批相同功能的类似铲装不同类型(小数,整数,等)。

有关参考 - 别处类中的键被定义为实际元组(只是与命名的参数),这就是为什么它可以作为一个字典键:

For reference - elsewhere in the class the key is defined as an actual Tuple (just with named parameters), which is why it can be used as a dictionary key:

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函数作为第三个参数,以我的扩展方法的想法。甚至不需要其限制为A + =操作了。这是通用的不够的任何操作都可以通过,如果一个值已经存在设置新的价值。

Thanks to Jon Skeet for the idea of passing a Lambda function as a third parameter to my extension method. No need to even restrict it to a += operation anymore. It's generic enough any operation can be passed to set the new value if a value already exists.

//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));

无尽​​的乐趣!

Endless fun!

推荐答案

首先,我建议不要做一切你可以做的东西尽可能短的可读性的潜在成本。例如,我会加括号围绕的foreach 体,如果一个更可读的解决方案,结束了两行,而不是一个,我会很高兴。

Firstly, I'd advise against doing everything you can to make something as short as possible at the potential cost of readability. For example, I'd add braces around the foreach bodies, and if a more readable solution ended up being two lines rather than one, I'd be happy with that.

其次,我会假设任何你感兴趣的类型,默认值是天然的零。

Secondly, I'm going to assume that for any of the types you're interested in, the default value is a natural zero.

现在,你可以这样写:

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

然后你可以使用:

Then you can use:

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

使用 ConcurrentDictionary 将工作做好了。

此外,我会尝试,如果你能返工这是一个LINQ查询。我也不会感到惊讶,如果 GROUPBY 总和的混合物 ToDictionary 允许你前preSS整件事声明。

Additionally, I would try to rework this as a LINQ query if you can. I wouldn't be surprised if a mixture of GroupBy, Sum and ToDictionary allowed you to express the whole thing declaratively.