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

C# 将值传递给动态按钮的事件处理程序,c#,C#,我试图在按钮单击事件上传递两个值 public MyClass() { Int64 po = 123456; foreach (Expense expense in pr.Expenses) { Button btnExpenseDetail = new Button(); btnExpenseDetail.Text = expense.ExpenseName; btnExpenseDetail.Location = new

我试图在按钮单击事件上传递两个值

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();
}
我正在使用VisualStudio2010C。在面板上,每个按钮的文本值都不同。但是按钮的点击事件的作用都是一样的。有人能告诉我这段代码的哪一部分是逻辑错误吗

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

此代码应该可以工作:

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();
}
让我们复习一些更基本的东西

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);
}
static void Main(字符串[]args)
{
var qs=新列表();
对于(变量i=0;i<10;i++)
添加(()=>f(“行为人”,i));
对于(变量i=0;i<10;i++)
qs[i]();
}
私有静态void f(字符串x,int y)
{
Console.WriteLine(“{0}:{1}”,x,y);
}
当您运行上面的代码时,您将始终得到输出:“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);
    }
}
private static void f(字符串x,int y)
{
Console.WriteLine(“{0}:{1}”,x,y);
}
私有静态void Main(字符串[]args)
{
List qs=新列表();
c_uuuDisplayClass1 CS$8_uuulocals2=新的c_uuuDisplayClass1();
CS$8_uulocals2.i=0;
而(CS$8_uulocals2.i<10)
{
添加(新操作(CS$8_ulocals2.b_u0));
CS$8_uulocals2.i++;
}
对于(int i=0;i<10;i++)
{
qs[i]();
}
}
[编译生成]
专用密封c类显示器Class1
{
//田地
公共国际一级;
//方法
公共无效b_u 0()
{
程序f(“实干家”,即本.i);
}
}
如您所见,编译器生成了一个名为
c\uu displayclas1
的类,并在进入循环之前初始化了它一次。在此之后,它只是增加变量
CS$8_ulocals2
i
属性

因此,当我在下一个循环中调用theeselambdas时,它使用
CS$8_ulocals2
对象来查找内部变量


(我的英语不是很好,尽管解释得不好,但都在那里…

看起来像是统计员常见的陷阱。基本上,如果对lambda使用枚举器变量(
在本例中为expense
),它总是在同一变量上创建闭包,因此它总是使用相同的值。您可以这样修复它:

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);
}
foreach(请购单费用中的费用)
{
var currentExpense=expense;//{MyHandler(发件人、e、po、currentExpense.ExpenseName);};
pnlProjectSummary_Expenses.Controls.Add(btnExpenseDetail);
}

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

这与C#的方式有关,请不要在标题前加上“C#”之类的前缀。这就是标签的用途。非常好用!也感谢您的详细和全面的解释。
Expense expense;
for expense in pr.Expenses
   // do processing
Int64 po = 123456;
foreach (Expense expense in pr.Expenses)
{
    var localExpense = expense;
    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 , localExpense.ExpenseName); };            
    pnlProjectSummary_Expenses.Controls.Add(btnExpenseDetail);
}