Przekazywanie parametru do EventHandlera

0

Cześć,

Mam taki kod:

int counter = 0;

for (int i = 0; i < height; i++)
{
	for (int j = 0; j < width; j++)
	{
		PictureBox picturebox = new PictureBox();
		picturebox.Name = "image_" + counter;
		picturebox.Height = 300;
		picturebox.Width = 400;
		picturebox.Load(_collection_of_streamers[counter].image);
		picturebox.Top = coords[i, j].X;
		picturebox.Left = coords[i, j].Y;
		///
		string link = _collection_of_streamers[counter].image;
		picturebox.Click += (sender, e) => Picturebox_Click(sender, e, link);
		///
		counter++;
	}
}
private void Picturebox_Click(object sender, EventArgs e, string link)
{
    Gallery gallery = new Gallery(link);
    gallery.Show();
}

I pytanie:

Dlaczego te dwa sposoby przekazania parametru do tego eventhandlera dają różne rezultaty?

Tutaj wszystko działa tak jak ma działać, każdy picturebox onClick dostaje inny obrazek:

string link = _collection_of_streamers[counter].image;
picturebox.Click += (sender, e) => Picturebox_Click(sender, e, link);

A tutaj jest ten sam obrazek w każdym.:

picturebox.Click += (sender, e) => Picturebox_Click(sender, e, _collection_of_streamers[counter].image);

chodzi o coś dot. typów referencyjnych/wartościowych?

1

Słowo klucz : domknięcie :) i to jak działa łapanie zmiennych do domknięcia.
w pierwszym wypadku każda lambda ma swoja kopie link'a, bo jest zdefiniowany wewnątrz pętli
a w drugim wypadku counter jest ten sam w każdej lambdzie; bo jest zdefiniowany na zewnątrz pętli

przykładzik na którym to ładnie widać

    static Action[] Foo1()
    {
        Action[] result = new Action[3];

        for (int i = 0; i < 3; i++)
        {
            result[i] = () => { Console.WriteLine(i); };
        }
        return result;
    }

    static Action[] Foo2()
    {
        Action[] result = new Action[3];

        for (int i = 0; i < 3; i++)
        {
            int j = i;
            result[i] = () => { Console.WriteLine(j); };
        }
        return result;
    }


    static void Main(String[] args)
    {
        foreach (var d in Foo1()) d();
        foreach (var d in Foo2()) d();
    }

Wynik to oczywiście
3 3 3 0 1 2

0
neves napisał(a):

Słowo klucz : domknięcie :) i to jak działa łapanie zmiennych do domknięcia.
w pierwszym wypadku każda lambda ma swoja kopie link'a, bo jest zdefiniowany wewnątrz pętli
a w drugim wypadku counter jest ten sam w każdej lambdzie; bo jest zdefiniowany na zewnątrz pętli

przykładzik na którym to ładnie widać

Ok, a dlaczego to działa poprawnie /// chociaż raczej nie powinno, bo usuwamy "w locie" foreachem

foreach (Control control in Controls.OfType<PictureBox>())
{
    Controls.Remove(control);
}

a to już nie?:>

foreach (Control control in Controls.OfType<Label>())
{
   Controls.Remove(control);
}
MessageBox.Show(Controls.OfType<Label>().Count().ToString()); /// shows 4

przykładowe obejście

List<Label> test = new List<Label>();

foreach (Object obj in this.Controls)
{  
	if (obj is Label)
	{
		test.Add((Label)obj);
	}
}
foreach (Label label in test)
{
	label.Dispose();
}
1

Usuwasz elementy z kolekcji po której iterujesz, to nigdy nie ma prawa się dobrze skończyć, końcowy efekt może być losowy. Jak już wiesz rozwiązaniem jest użycie kopi kolekcji z elementami które chcemy usunąć. I to nie ma nic wspólnego z domknięciami bo one tutaj nie występują.

W skrócie można to zapisać też tak:

foreach (Control control in Controls.OfType<Label>().ToList())
{
   Controls.Remove(control);
}

1 użytkowników online, w tym zalogowanych: 0, gości: 1