复杂的 LINQ 请求和奇怪的 int 增量行为

Complex LINQ request and odd int increment behavior

我正在努力处理一个复杂的 LINQ 请求。

我在助手 class (ColorTools) 中创建了一个包含 8 种颜色的列表。我想将这些颜色作为对象实例化参数传递。

知道我最多创建了 5 个这些对象,因此我创建了一个计数器,每次我创建其中一个对象并在我的列表中获得相应的颜色时,我都会递增该计数器。

但是我得到了一个 OutOfRangeException。

Dictionary<ItemVM, int> topItem = DisplayedAlarmOccurrences
    .Select(ao => ao.Item)
    .GroupBy(t => t)
    .ToDictionary(t => t.Key, t => t.Count())
    .OrderByDescending(t => t.Value)
    .Take(5)
    .ToDictionary(t => t.Key, t => t.Value);

//topItem countains up to 5 elements here
int cpt = 0;

int maxOccItem = topItem.Any() ? topItem.Values.Max() : 10;
SummaryParetoVM summaryItem = new SummaryParetoVM(
    "Items",
    new Axis("Number of Items", 0, maxOccItem),
    topItem.Select(t => new ParetoSerie(
        new []{
            new ParetoInformationPlacement(Place.MIDDLE_CENTER,
                new InformationPlacement()
                {
                    Information = "n° ",
                    Value = t.Key.Index.ToString()
                }),
                new ParetoInformationPlacement(Place.RIGHT_CENTER,
                    new InformationPlacement()
                    {
                        Information = null,
                        Value = t.Value.ToString()
                    }
                )
            }
        )
        {
            //cpt will have a value of 8 here, how is it possible when topItem only got up to 5 items?
            Points = new[]
            {
                new ParetoValuePoint(t.Value,
                    t.Key.Index.ToString(),
                    ColorTools.ChartingBrushes[cpt++])
            }, 
            Title = "Top Items"
        }
    ).Cast<Serie>()
);

所以异常发生在我传递

的 ParetoValuePoint 实例中
ColorTools.ChartingBrushes[cpt++] 

作为 ParetoValuePoint 构造函数的参数。

我尝试使用 Interlocked.Increment 增加 cpt,但 cpt 将增加到 8 并且异常以相同的方式出现。

注意:这只是一个测试代码,所以代码的整洁度在这里无关紧要,我只想知道计数器的值是如何超过 5 的。

编辑 ChartingBrushes 是一个 public 静态只读列表,它在静态 class 的静态构造函数中启动。这是代码:

public readonly static List<Brush> ChartingBrushes;
static ColorTools() {
    ChartingBrushes = new List<Brush>{
        new SolidColorBrush(Color.FromRgb(115, 115, 115)),
        new SolidColorBrush(Color.FromRgb(241, 90, 96)),
        new SolidColorBrush(Color.FromRgb(122, 195, 106)),
        new SolidColorBrush(Color.FromRgb(90, 155, 212)),
        new SolidColorBrush(Color.FromRgb(250, 167, 91)),
        new SolidColorBrush(Color.FromRgb(158, 103, 171)),
        new SolidColorBrush(Color.FromRgb(206, 112, 88)),
        new SolidColorBrush(Color.FromRgb(215, 127, 180))
    };

    ChartingBrushes.ForEach(b => b.Freeze());
}

枚举不是 'closed'。即每次枚举增加cpt。如果您要添加 ToList(),则枚举只会 运行 一次,并且会创建一个包含结果的列表:

.Cast<Serie>().ToList()

除此之外,还有一个采用索引的 Select 重载,可防止对变量的依赖。通过使用索引,问题也可以避免,但每个对象都会在每个枚举上创建。最好是重载和 ToList 的组合。

不创建列表或其他集合的示例

    int[] values = new int[4];
    int cntr = 0;

    var enumerable = values.Select(i => cntr++);
    foreach (var i in enumerable)
        Console.WriteLine(i);

    foreach (var i in enumerable)
        Console.WriteLine(i);

    //at this point counter is 8, because the enumeration has run twice. If ToList would be appended to the Select, the result would be 4. 

组合 ToList 和带有索引的 Select 的示例:

             int[] values = new int[4];
             var list = values.Select((value, index) => index).ToList();