Laat ik beginnen om mezelf even voor te stellen. Mijn naam is Peter van Ooijen en ik ben software ontwikkelaar en bij deze nu ook blogger op 4dotnet. Sommigen van jullie kennen mij misschien van mijn weblog op Codebetter. Of misschien nog van vroegere artikelen in het SDN magazine. Ik heb dus al een weblog. Een daar blijf ik ook schrijven. Waarom begin ik er hier dan nog ééntje ? En waarom hier bij 4dotnet ? Daar zijn verschillende redenen voor. Ten eerste zitten zowel DTS, de thuisbasis van 4dotNet, als ik in het Noorden van Nederland. Voor menig Westerling is dat ergens voorbij de achterkant van de maan, maar vergis je niet, hier gebeurt zoveel meer dan jullie denken. Om over onze leefomgeving maar te zwijgen. Nog een reden voor deze blog is dat het hier gewoon in het Nederlands kan. Is ook wel eens lekker in die enorme zee van buzzwords. Maar de belangrijkste reden voor deze blog is de gedeelde achtergrond van 4dotnet en mij. Beiden komen we uit de Delphi hoek en hebben we van daaruit C# en .NET ontdekt.
In een grijs verleden heb ik nog mogen werken met dBase III en Turbo Pascal. Je had dBase nodig om een invoerscherm te maken of gegevens op te kunnen slaan. Je had Turbo Pascal nodig om echt te kunnen programmeren. Het waren twee werelden, in dBase kon niet eens een eigen functie schrijven en in TurboPascal had je geen textboxen of database tables. dBase werd Clipper en Turbo Pascal werd Delphi. Met Delphi had je eindelijk een tool waarmee alles mogelijk was. Een fijne programmeertaal, rijke Windows UI, krachtige controls voor het maken van in en uitvoerschermen en een echte database er onder. De kracht van Delphi zat hem naast de taal in de VCL, een bibliotheek vol functionaliteit die dankzij de object georiënteerde aanpak zelf aan te passen en uit te breiden is.
Turbo Pascal en Delphi zijn beiden gemaakt door Anders Hejlsberg. Hij ging na Delphi 3 bij Microsoft werken en kreeg daar de kans om zijn ideeën van de grond af opnieuw op te bouwen. Delphi blijft Pascal waar later object oriëntatie bovenop is gebouwd. C# had geen geschiedenis mee te torsen en kon vanaf het fundament worden gericht op Object orientatie. De VCL is een mooie library maar hij moest wel in elke applicatie worden meegelinkt. De runtime omgeving van deze applicatie is het OS zelf. Het .NET framework zit op een veel logischer plaats dan de VCL, tussen applicatie en OS in. Een .NET applicatie heeft een rijke runtime omgeving. De CLR isoleert je applicatie van het OS en biedt heel veel functionaliteit voor het gebruik van libraries, het beheer van (oa) geheugen en het beschermen van je applicatie tegen al dan niet voorzien onheil.
De beta’s van .NET vielen in de Delphi 6 periode. In die tijd was ik zelf veel bezig met COM in Delphi. Daarmee kon je ook tegen .NET code aan praten en dat ben ik toen meteen gaan bekijken. Om binnen de kortste keren helemaal verliefd te worden op Visual Studio 2002 en C# 1.0. Voor mij was het duidelijk dat de “Spirit of Delphi” met Anders mee was verhuisd naar Microsoft. Verhalen dat MS een evil empire is zeggen mij persoonlijk niet zo veel. Natuurlijk hebben producten als VB6 en Windows 98 niet echt bijgedragen aan het imago. Maar ook bij (destijds) Borland was het niet zo fris. Het succes van Delphi had tot een zekere arrogantie geleid waarbij niet technische argumenten de boventoon gingen voeren en de technische implementatie zelf begon te rammelen. Wie in die tijd met Delphi webservice clients heeft gemaakt kan zich dit misschien nog wel herinneren.
Maar goed, ik was dus een vroege bekeerling van .NET, vanuit Delphi gezien bleek ik later een “early defector”. Maar voor mij is het een natuurlijk vervolg geweest.
Als kleine illustratie hiervan wil ik hier events en eventhandlers bespreken. Voor diegenen die met een VB of C++ achtergrond in .NET terechtkwamen was dat nieuw, voor wie met een Delphi achtergond in .NET terechtkwam was het een feest van herkenning.
Events zijn gebeurtenissen in je applicatie, zoals de gebruiker die een knop klikt. Een eventhandler is de code die dan uitgevoerd wordt. Het vooruitstrevende van Delphi was dat de taal zelf deze twee begrippen in de taal kent. Neem het volgende stukje code
procedure MyEventHandler(sender : object)
begin
ShowMessage(‘You clicked the button’);
end;
MyButton.OnClick:= MyEventHandler;
De procedure bevat de code die uitgevoerd moet worden, het is de eventhandler. De knop MyButton heeft een event property OnClick. In code wordt de eventhandler gekoppeld aan de event door de event property toe te kennen.
Een procedure heeft een zogeheten signature, deze handtekening beschrijft het return type van de procedure en de parameters. Zo’n signature is een type dat als volgt wordt gedeclareerd:
type
TNotifyEvent = procedure(sender: Tobject) of object;
TNotifyEvent is eentype dat een procedure met een object als parameter beschrijft. Dit type gebruik je voor het declareren van je events. Waar dan de handlers vanuit (al dan niet door de form designer gegenereerde) code aangekoppeld worden.
Deze ontkoppeling tussen event en handler is op zichzelf prachtig. Maar de Delphi implementatie heeft één heel groot nadeel, je kan maar één handler aan een event koppelen. Zodra je een tweede handler toekent aan het event wordt de eerste handler niet meer uitgevoerd.
Ook het .net framework kent events en eventhandlers. Daarin zijn ze vanaf het begin beter uitgewerkt. Voor het declareren van een signature is de delegate geïntroduceerd.
delegate void NotifyEvent(object sender);
Het framework zelf bevat meerdere delegates, de bekendste is System.EventHandler
delegate void EventHandler(object sender, System.EventArgs e);
Events in C# volgen het “publish en subscribe” model. De eventhandler wordt gepubliceerd (publish) door een delegate property te decalreren. Een methodes die moet worden uitgevoerd als het event af gaat kan zich abonneren (subscribe) op zo’n delegate proerty.
In dit voorbeeld worden er zo twee procedures gekoppeld aan de klik van MyButton.
private void MyClick1(object sender, System.EventArgs e)
{
MessageBox.Show(“You clicked the button”);
}
private void MyClick2(object sender, System.EventArgs e)
{
MessageBox.Show(“And I’ll tell you twice”);
}
MyButton.Click += new System.EventHandler(MyClick1);
MyButton.Click += new System.EventHandler(MyClick2);
Het subscriben gaat door het maken van een nieuw System.EventHandler object. De constructor krijgt de uitvoerende procedure mee. Op deze manier kan je met de += operator subscriptions toevoegen. Met de -= operator kan je subscriptions weer beëindigen.
Dit is een krachtige uitbreiding op het oorspronkelijke Delphi model. Er is nu de mogelijkheid meerdere handlers aan één event te koppelen. En daarnaast zijn deze handlers objecten als alle andere objecten in het framework. Ze zijn dus managed en type safe. Wie iets als een C++ achtergrond heeft en nog gewend is keiharde pointers te zetten zal dat helemaal kunnen waarderen. De .NET CLR scheelt hier menige GPF of blauw scherm.
Maar de ontwikkeling van events in .NET is hier niet opgehouden. De code van de meeste eventhandlers bestaat uit maar uit een paar regels. Het zou je code een stuk overzichtelijker maken als je de toekenning van de handler en de code van de handler zou kunnen combineren. In eerste instantie werden dat de anonieme functies, een verrijking van C# geïntroduceerd in versie 2.
Nu volgt de oorspronkelijke code in zijn minimale vorm. Als je hem vergelijkt met het eerste voorbeeld dan zie je dat er niet expliciet een new System.EventHandler object wordt aangemaakt, het voldoet om de methode toe te voegen. Achter de schermen worden de verdere details geregeld.
private void setHandler1()
{
button1.Click += button1_Click;
}
private void button1_Click(object sender, EventArgs e)
{
label1.Text = string.Format(“Clicked by {0} args {1}”, sender, e);
}
Deze twee methodes kunnen worden samengevoegd met behulp van een anonymous method
private void setHandler2()
{
button1.Click += (delegate(object sender, EventArgs e) { label1.Text = string.Format(“Clicked by {0} args {1}”, sender, e); });
}
Dit ziet er nogal cryptisch uit. Het click event ontvangt een delegate met de signature (object, EventArgs). Dit delegate object heeft geen naam, het is anoniem. De uit te voeren code staat tussen de { } haken. Zoals je ziet zijn zowel de parameters als het Label control vanuit deze code benaderbaar.
Wie ooit met Clipper heeft gewerkt zal in deze constructie misschien nog wel het codeblock herkennen.
Het voordeel van de anonymous method is dat we van de methode button1_click af zijn, het nadeel is dat de syntax cryptisch is. Dat laatste lossen lambda expressies op, nieuw in C# 3.
Precies dezelfde code kan je zo als lambda expressie uitschrijven
private void setHandler3()
{
button1.Click += ((sender, e) => label1.Text = string.Format(“Clicked by {0} args {1}”, sender, e));
}
Alle types worden hier weggelaten. Je geeft een lijst van de parameters, dan de => en dan meteen de uit te voeren code. Het is niet zo dat deze code niet getypeerd is, alle types worden door de compiler afgeleid uit hun gebruik. Omdat de lamda wordt toegevoegd aan een button click event zal hij de signature moeten hebben van een system.Eventhandler. De parameters daarvan zijn een sender en e. De code in de lamda herkent deze en hun type: object en System.EventArgs.
Tot zover deze reis door de tijd. Ik hoop dat dit een beetje laat zien dat er in het schrijven van code aan de ene kant veel veranderd is, maar dat al het nieuwe wel gebouwd is op een rijk verleden. En deze reis is nog lang niet afgelopen, ik hoop er in de komende tijd nog meer over te mogen schrijven.

