将值传递给动态按钮的事件处理程序

SiH*_*Lee 3 c#

我正在尝试在Button_Click事件上传递两个值

public MyClass()
{
    Int64 po = 123456;
    foreach (Expense expense in pr.Expenses)
    {
        Button btnExpenseDetail = new Button();
        btnExpenseDetail.Text = expense.ExpenseName;
        btnExpenseDetail.Location = new Point(startLocation.X + 410, startLocation.Y + (23 * 
        btnExpenseDetail.Click += (sender, e) => { MyHandler(sender, e, po , expense.ExpenseName); };            
        pnlProjectSummary_Expenses.Controls.Add(btnExpenseDetail);
    }
}  


void MyHandler(object sender, EventArgs e, string po, string category)
{
    FormExpenseDetails ed = new FormExpenseDetails(po, category);
    ed.Show();
}
Run Code Online (Sandbox Code Playgroud)

我正在使用visual studio 2010 c#.在面板上,每个Button的文本值都是不同的.但按钮'Click_Events的行为完全相同.有人能告诉我哪部分代码我得到这个逻辑错误?

================================================== ======================

Bot*_*000 6

看起来像普查员常见的陷阱.基本上,如果expense对lambda 使用枚举器变量(在本例中),它总是在同一个变量上创建一个闭包,因此它总是使用相同的值.你可以像这样解决它:

foreach (Expense expense in pr.Expenses)
{
    var currentExpense = expense; // <-- This should help. Also use this variable for the lambda.
    Button btnExpenseDetail = new Button();
    btnExpenseDetail.Text = currentExpense .ExpenseName;
    btnExpenseDetail.Location = new Point(startLocation.X + 410, startLocation.Y + (23 * 
    btnExpenseDetail.Click += (sender, e) => { MyHandler(sender, e, po , currentExpense.ExpenseName); };            
    pnlProjectSummary_Expenses.Controls.Add(btnExpenseDetail);
}
Run Code Online (Sandbox Code Playgroud)

您可以将lambda视为传递对变量的引用expense.即使变量的值随每次迭代而变化,引用仍指向同一变量.这就是为什么它有助于为每次迭代(currentExpense)创建一个本地范围的变量.字符串值和位置不同,因为它们被分配到每次迭代的另一个位置(Button.Text,Button.Location).


Med*_*kal 5

这段代码应该有效:

public MyClass()
{
    Int64 po = 123456;
    foreach (Expense expense in pr.Expenses)
    {
        var expenseName = expense.ExpenseName;
        Button btnExpenseDetail = new Button();
        btnExpenseDetail.Text = expense.ExpenseName;
        btnExpenseDetail.Location = new Point(startLocation.X + 410, startLocation.Y + (23 * 
        btnExpenseDetail.Click += (sender, e) => { MyHandler(sender, e, po, expenseName); };            
        pnlProjectSummary_Expenses.Controls.Add(btnExpenseDetail);
    }
}  


void MyHandler(object sender, EventArgs e, string po, string category)
{
    FormExpenseDetails ed = new FormExpenseDetails(po, category);
    ed.Show();
}
Run Code Online (Sandbox Code Playgroud)

让我们来看看更基本的东西.

static void Main(string[] args)
{
    var qs = new List<Action>();

    for (var i = 0; i < 10; i++)
        qs.Add(() => f("doer", i));

    for (var i = 0; i < 10; i++)
        qs[i]();

}

private static void f(string x, int y)
{
    Console.WriteLine("{0}: {1}", x, y);
}
Run Code Online (Sandbox Code Playgroud)

当你运行上面的代码时,你总是得到输出:"doer:10".让我们反编译代码:

private static void f(string x, int y)
{
    Console.WriteLine("{0}: {1}", x, y);
}

private static void Main(string[] args)
{
    List<Action> qs = new List<Action>();
    <>c__DisplayClass1 CS$<>8__locals2 = new <>c__DisplayClass1();
    CS$<>8__locals2.i = 0;
    while (CS$<>8__locals2.i < 10)
    {
        qs.Add(new Action(CS$<>8__locals2.<Main>b__0));
        CS$<>8__locals2.i++;
    }
    for (int i = 0; i < 10; i++)
    {
        qs[i]();
    }
}

[CompilerGenerated]
private sealed class <>c__DisplayClass1
{
    // Fields
    public int i;

    // Methods
    public void <Main>b__0()
    {
        Program.f("doer", this.i);
    }
}
Run Code Online (Sandbox Code Playgroud)

如您所见,编译器c__DisplayClass1在进入循环之前生成了一个名为并初始化的类.之后,它只增加i了变量的属性CS$<>8__locals2.

所以当我在下一个循环中调用theese lambdas时,它会使用该CS$<>8__locals2对象来查看内部变量.

(我的英语不是很好解释它,但它都在那里...)