Lambda表达式无法设置某些控件的事件处理程序

本文关键字:控件 事件处理 程序 表达式 设置 Lambda | 更新日期: 2023-09-27 17:59:50

我正在创建一个控件数组并将它们添加到表单中,并将它们的事件设置为一个函数,该函数使用lambda表达式(b.Click += (sender, e) => myClick(i);)接收单击按钮的索引。

但问题是……无论你点击哪一个,你都会收到索引100,而不是按钮的真正索引!这里有什么问题?

namespace testArrayOfControls
{
    public partial class Form1 : Form
    {
        Button[] buttons;
        public Form1()
        {
            InitializeComponent();
            buttons = new Button[100];
            for (int i = 0; i < 100; i++)
            {
                buttons[i] = new Button();
                buttons[i].SetBounds(i % 10 * 50, i / 10 * 50, 50, 50);
                buttons[i].Click += (sender, e) => myClick(i);
                this.Controls.Add(buttons[i]);
            }
        }
        private void myClick(int i)
        {
            MessageBox.Show(i.ToString());
        }
    }
}

Lambda表达式无法设置某些控件的事件处理程序

问题是在循环变量i上创建闭包。在将它传递给事件处理程序之前,您需要制作它的本地副本(在for循环内)。

for (int i = 0; i < 100; i++)
{
    var index = i; // YOU NEED TO DO THIS
    buttons[i] = new Button();
    buttons[i].SetBounds(i % 10 * 50, i / 10 * 50, 50, 50);
    buttons[i].Click += (sender, e) => myClick(index); // THIS SOLVES THE PROBLEM
    this.Controls.Add(buttons[i]);
}

解释

您正在定义这样一个函数:

(sender, e) => myClick(i)

此函数(将在将来某个时候单击按钮时运行)包含对i的引用。其工作方式是,它将在单击发生时使用i的值,而不是在定义函数时。

到那时,显然i的值将变为100。

该解决方案之所以有效,是因为它使函数引用变量index而不是iindexi的不同之处在于,i是一个值发生变化的变量,而index是我们用于100个不同变量的名称(每个循环迭代一个),其值保持不变

问题在于修改的闭包。以下是@Jon Skeet对该主题的精彩解释。

int index = i; 
buttons[i].Click += (sender, e) => myClick(index);

试试看。

不幸的是,闭包(在我们的例子中是变量i)在C#中不能正常工作。更换

b.Click += (sender, e) => myClick(i);

带有

Action<int,Button> act = (index,b) => { b.click += (sender, e) => myClick(index) }
act(i,b);