Możliwość używania delegatów w C# to fajna rzecz. Przyjemne jest zwłaszcza definiowanie ich “w locie”, czyli bez konieczności tworzenia zupełnie nowej funkcji. Takiego delegata nazywamy wówczas anonimowym:
Przydaje się to zwłaszcza to podawana różnego rodzaju predykatów do funkcji sortujących lub wyszukujących. Takie nienazwane funkcje są zwykle krótkie i bardzo proste.
A co jeśli jest inaczej?… W szczególności interesująca sytuacja jest wtedy, gdy nasz delegat odwołuje się do zmiennej zewnętrznej – czyli takiej, która nie została w nim zadeklarowana, ale której zasięg zawiera definicję delegata. Oto przykład:
Powyższy kod (skompilowany pod .NET co najmniej 3.0) pokaże 0 i 1, bowiem anonimowy delegat wiązany jest z samą zmienną i
, nie zaś jej wartością w momencie definicji funkcji (czyli 0
). Że zachowanie to nie jest znowu tak oczywiste, można uzasadnić podając przykład biblioteki Boost.Lambda dla C++, gdzie jest odwrotnie. Tam domyślnie wiązane są same wartości zmiennych zewnętrznych (w momencie tworzenia anonimowej funkcji), ale można to zmienić niewielkim wysiłkiem (używając modyfikatora var
, jeśli kogoś to interesuje).
W C# podobnej możliwości nie ma, a do anonimowych delegatów wiązane są zawsze same zmienne, a nie ich wartości. Jeśli jednak potrzebowalibyśmy czegoś takiego, to prostym wyjściem jest wprowadzanie zmiennej pomocniczej, zainicjowanie jej wartością zmiennej pierwotnej i używanie jej wewnątrz delegatu:
Zarówno delegata, jak i deklarację owej zmiennej dobrze jest też zamknąć w osobnym bloku kodu – tak jak powyżej. Dzięki temu eliminujemy możliwość przypadkowej jej modyfikacji, która oczywiście zostałaby “zauważona” przez delegata.
Nie spotkałem się z terminem “wiązanie”. Po angielsku mówi się zwykle na to “closure”, po polsku słyszałem “domknięcie”. Ta druga nazwa też obowiązuje?
Strasznie przyjemny mechanizm, BTW. Bardzo popularny w językach skryptowych, choćby Pythonie, a najczęściej się go stosuje w Javascript – “emuluje się” tam w ten sposób klasom pola prywatne. :)
Nazwa “domknięcie” jest czasem spotykana, ale niezbyt częsta. Jeśli chcemy się spierać o terminologię, to formalnie closure oznacza samą anonimową funkcję, która ma jakieś zmienne zewnętrzne. Ja tu użyłem bardziej ogólnego terminu ‘wiązanie’, bo chodziło mi o relację takiej funkcji zarówno z zewnętrznymi zmiennymi (domyślny przypadek w C#), jak i wartościami tych zmiennych (to, co w C# można niejako wymusić trikiem opisanym pod koniec, lub co jest domyślne w Boost.Lambda).
Co do podobnego mechanizmu w JavaScripcie… Domyślam się jak to może pomagać w symulowaniu prywatnych pól, ale nie obraziłbym się na jakiś przykładowy kawałek kodu :) (Jeju co ja mówię, z własnej woli chcę oglądać kod JavaScript o.0).
button1.Click += (a, b) => MessageBox.Show("T");
Tak ładniej ;->
Xion: Niech kod posłuży za obrazek :) Ofc to jest jedyne jedno podejście z możliwych. Javascript jest sam w sobie dość surowy, ale pozwala ‘stworzyć sobie’ model obiektowy wedle uznania.
function Klasa(x) { // funkcja-konstruktor
var a = arg;
function jakasPrywatnaFunkcja() {
alert("nikt o mnie nie wie!");
}
// tworzymy funkcję (funkcja w js = first-class value)
function getValue() {
return a;
}
return { // zwracamy obiekt, czyli w sumie tablicę asosjacyjną
// albo pośrednio: tak:
getValue: getValue,
// albo od razu:
setValue: function(x) {
a = x;
}
}
// zmienna 'a' jest prywatna, bo jest związana z obiektem jedynie poprzez closures jego funkcji
}
Długi komentarz mi się nie chciał pojawić (mimo że przy drugiej próbie wklejenia usłyszałem, że duplikat), dobrze działa Twój blog?
Wrzucę zatem kod na nopaste:
http://www.nopaste.pl/o53-javascript
Przypomnę że jest to tylko jedno możliwe podejście – JS to zabawny luźny język pozwalający sobie “pokombinować” z robieniem swojego OOP-u. :)
Naciąłeś się na przewrażliwiony filtr antyspamowy. Rzadko ma fałszywe pozytywy, ale czasem się zdarzają :) A co do tego kodu, to wygląda mi to na gorszą wersję obiektowości z Pythona… a przypominam, że tam hermetyzacji nie ma w ogóle ;-)