Delegate is using the wrong value

J

james

(Sorry if this is a double post)

Hi Guys,

I am creating a delegate a couple times and passing in a local
variable. However, when the delegate is invoked it uses the variable
passed into the last created instance (sorry that was a mouthful).
Here is an example from my code:

private void dlgFavorites_Load(object sender, EventArgs e)
{
Hashtable bookmarks =
ConfigurationSettings.GetConfig("Bookmarks") as Hashtable;

if(bookmarks == null)
return;

int i = 0;
foreach (DictionaryEntry pair in bookmarks)
{
Button btn = new Button();
btn.DialogResult = DialogResult.OK;
btn.Text = (string)pair.Key;

btn.Click += new EventHandler(delegate
{
m_selectedUrl = (string)pair.Value;
});
btn.Dock = DockStyle.Fill;

tableLayoutPanel1.Controls.Add(btn, i%2, (int)(i/2));

++i;
}
}

I was able to get around the problem by doing:

string value = (string)pair.Value;
btn.Click += new EventHandler(delegate
{
m_selectedUrl = value;
});

So I think it has something to do with boxing ValueTypes. Anyway I
would love to hear any ideas.

Thanks,
James
 
B

Barry Kelly

james said:
I am creating a delegate a couple times and passing in a local
variable. However, when the delegate is invoked it uses the variable
passed into the last created instance (sorry that was a mouthful).
foreach (DictionaryEntry pair in bookmarks)

There is a single variable called 'pair', and its value changes as the
loop progresses. In particular, it doesn't create a new variable once
per iteration. This is important when the semantics of anonymous
delegate variable capture are considered.
{
Button btn = new Button();
btn.DialogResult = DialogResult.OK;
btn.Text = (string)pair.Key;

btn.Click += new EventHandler(delegate
{
m_selectedUrl = (string)pair.Value;

Here, you have captured the variable called 'pair'. That means that when
this delegate gets executed, it reads the value that is currently in
pair, whatever it is.

And because this Click event presumably won't be executed until much
later, after this loop has finished executing, it will have the value of
the last item in the loop.

And since all Click events refer to the same variable 'pair', they'll
all refer to the same value, its final value.
});
btn.Dock = DockStyle.Fill;
string value = (string)pair.Value;

This creates a new variable that is local to the loop and is created
fresh and new each time around the loop.
btn.Click += new EventHandler(delegate
{
m_selectedUrl = value;
});

So I think it has something to do with boxing ValueTypes. Anyway I
would love to hear any ideas.

No, it doesn't have to do with boxing, but with variable capture. It's
important to know that anonymous delegates capture variables, not
values.

-- Barry
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top