如何使用 Net Framework 在 C# 中管理数千个级联事件?

How to manage thousands of cascade events in C# with Net Framework?

我在 .Net Framework 4.8 中有一个应用程序,其中有一个十进制值列表,我想在任何此列表成员的值发生变化时执行计算,代码工作正常,除非列表数据太大。

我可以使用哪种数据结构或方法来避免堆栈溢出?有解决这个问题的最佳实践或技巧吗?

我在一个小型控制台应用程序中准备并重现了错误:

class Program
{
    static void Main(string[] args)
    {

        List<ContainerModel> containerModels = new List<ContainerModel>();

        PopulateModel(containerModels, 25000);

        SubscribeVariables(containerModels);

        containerModels.First().Index = 5000;

        foreach (ContainerModel item in containerModels)
        {
            Console.WriteLine(item.Index);
        }

        Console.ReadLine();
    }

    private static void PopulateModel(List<ContainerModel> containerModels, int numberOfVariables)
    {
        for (int i = 0; i < numberOfVariables; i++)
        {
            containerModels.Add(
                new ContainerModel()
                {
                    Index = i
                }
            );
        }
    }

    private static void SubscribeVariables(List<ContainerModel> containerModels)
    {
        for (int i = 0; i < containerModels.Count() - 1 ; i++)
        {
            containerModels[i].ValueChanged += containerModels[i + 1].C_ValueChanged;
        }
    }
}

public class ContainerModel
{
    public event EventHandler<decimal> ValueChanged;

    private decimal _index;
    public decimal Index
    {
        get => _index;
        set
        {
            _index = value;

            ValueChanged?.Invoke(this, value);

        }
    }
    public void C_ValueChanged(object sender, decimal value)
    {
        Index += value;
    }
}

以上代码产生堆栈溢出异常。

我非常感谢能为我指明正确方向的任何提示或信息

这种带有“ContainerModel”class 的模型在值更改时引发事件并不少见,即使我更喜欢通用实现。我还建议仅在值实际发生变化时才引发事件。这有助于避免不必要的更新。

然而,链接一长串对象的事件处理程序的模型可能不是一个好主意。如果您有需要监视更改的对象列表,最好创建一个新的集合类型来处理值的更改,并使用常规循环执行您需要的任何更新逻辑。

public class MyCollection
{
    private List<decimal> values = new ();
    public void Add(decimal v) => values.Add(v);

    public decimal this[int index]
    {
        get => values[index];
        set
        {
            values[index] = value;
            for (int i = index+1; i < values.Count; i++)
            {
                values[i] += values[i - 1];
            }
        }
    }
}

如果您需要连接不同的集合,则向集合添加事件以在值范围发生更改时通知其他集合。

在不了解应用程序上下文的情况下,很难提供更多更好的建议。另一种方法是将关系建模为图形,并使用图形遍历算法来应用您想要的任何更新逻辑。这样你就可以在遍历时使用显式堆栈,并且只限于计算机的内存。