søndag den 19. maj 2013

Hændelser

De foregående indlæg har behandlet delegates, der er grundlaget for hændelser eller events.I det følgende forklares events udfra hvad der sker ved tryk på en knap.

En knap, en checkboks og andre elementer i brugerfladen i .NET er i virkeligheden klasser. Disse klasser indeholder strukturer der hedder events.

Det er ikke knappens klasse der bestemmer hvad der skal ske når der f.eks trykkes på knappen. Eventen giver blot besked til metoden / metoderne der "lytter" om at handlingen er foretaget. Det er så den lyttende kode der bestemmer hvilke metoder der skal kaldes. Lyttende metoder siges også at abonnere på hændelsen.

Man taler om Subscriber og Broadcaster.

Subscriber - Lyttende kode
I praksis laver man i den kaldende kode en metode, og lader denne metode abonnere på en event.

Man abonnerer på eventen som vi så i indlæg om delegates ved at bruge += operatoren på delegaten. Hermed knytter man i afviklingsøjeblikket de metoder der skal afvikles ved hændelsen.

Broadcaster - Knappen
Knappen bestemmer hvornår hændelsen skal ske, ved at kalde delegaten. Man definerer en hændelse med event ordet. Events er en hjælpe funktionalitet i C# En event har en type og et navn. Eventen defineres i knappens klasse.

Typen er en delegate, som vi selv definerer, eller man kan anvende C#'s  indbygget delegate der hedder EventHandler. EventHandler har to argumenterne (object sender, EventArgs e).

Eventen defineres som sagt i knappens klasse, knappens objekt sendes sammen med argumentet e til den metode / metoder der skal foretage sig noget.

Koden der afvikles
Koden der afvikles ligger typisk udenfor selve knappen. Ser vi i  visual studio på en typisk signatur til en metode der bliver afviklet når der klikkes på en knap, kan den se således ud:

 protected void Button1_Click(object sender, EventArgs e)

Visual Studio laver netop en event med typen EventHandler delegate automatisk. Argumenterne er det objekt der har udløst eventen sender(knappen), samt eventuelle argumenter i et EventArgs objekt e.

lørdag den 18. maj 2013

Multicast Delegates

Alle delegate objekter har mulighed for at pege på ikke kun en men flere metoder. Dette kaldes multicast delegates. Et delegate objekt  kan altså vedligeholde en liste af metoder.

I indlægget Delegates knyttede vi metoden Velkommen.Tysk til delegaten Dele ved at bruge den som argument da vi instansierede delegaten.

VelkomstDelegate Dele = new VelkomstDelegate(Velkomst.Tysk);

Der er flere forskellige syntakser for at tilknytte endnu en metode til delegaten. En syntaks til at tilknytte metoden Velkomst.Engelsk kunne være:

Dele = Dele + Velkomst.Engelsk;

Typisk vil man bruge følgende syntaks der sematisk er det sammen som ovenstående:

Dele += Velkomst.Engelsk;

På samme måde kan man bruge - og -= operatorene til at fjerne metoder fra listen over metoder delegaten refererer til.

Man kan også assigne null til en delegate

Dele = null;

Delegates er immutable, så når man bruger operatorer som += på denne laver man i virkeligheden et nyt objekt i hukommelsen og peger delegaten på dette.

Har multicast delegaten en returtype er det retur værdien fra den sidste metode der returneres til den kaldende kode.




Delegates


Brugen af hændelser eller Events i C# er en vigtig del af brugerfladeprogrammering. Hændelser giver en klasse mulighed for at kommunikere med andre objekter. Dette betyder at hændelser kan reagere på en handling i brugergrænsefladen, det kan f.eks være tryk på en knap, markering af en checkbox etc.

Grundlaget for hændelser er de såkaldte delegates. Delegates er referncevariabler der gør det mulighed at at kalde en metode uden at definere hvilken metode der kaldes før i afviklingsøjeblikket. En delegate anvender keywordet delegate. En delegate skal have samme signatur som den metode den skal bruges til at kalde. Det vil sige samme returtype og samme antal og type argumenter.

Nedenstående eksempel viser en delegate i funktion.

    // Delegaten defineres
    public delegate void VelkomstDelegate(string navn);

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Indtast navn");
            string navn = Console.ReadLine();

            Console.WriteLine("Indtast sprog");
            string sprog = Console.ReadLine();         

            if (sprog == "tysk")
            {
                // Der laves et objekt af delegaten VelkomstDelegate, metoden bruges som argument her Velkomst.tysk
                VelkomstDelegate Dele = new VelkomstDelegate(Velkomst.tysk);

                // Delegaten bruges nu som metoden Velkomst.tysk ville være brugt
                Dele(navn);
            }
            else
            {
                // Der laves et objekt af delegaten VelkomstDelegate, metoden bruges som argument her Velkomst.engelsk
                VelkomstDelegate Dele = new VelkomstDelegate(Velkomst.engelsk);

                // Delegaten bruges nu som metoden Velkomst.engelsk ville være brugt
                Dele(navn);
            }
            Console.ReadKey();
        }
    }

    public class Velkomst
    {
        public static void tysk(string navn)
        {
            Console.WriteLine("Wilkommen {0}", navn);
        }
        public static void engelsk(string navn)
        {
            Console.WriteLine("Welcome {0}", navn);
        }
    }

Ovenstående eksempel er ikke perfekt i forhold til at illustrere det ekstra lag af fleksibilitet som delegates giver, da det er det samme stykke kode der laver delegaten som bruger den. Man skal forestille sig at en metode laver delegaten og en anden metode bruger den. Dette giver mulighed for at kalde en metode via en delegate og først runtime, altså i afviklingsøjeblikket at bestemme præcis hvilken metode delegaten kalder.

I ovenstående eksempel bruges delegate objektet til at kalde en metode. Delegate objektet kan også bruges som argument i en metode eller hændelser som beskrevet i starten af dette indlæg.

lørdag den 11. maj 2013

Valuevariabler og referencevariabler

I c# arbejder man med to typer variabler; referencevariabler og valuevariable.

Valuevariabler

Valuevariabler er structures der gemmes på stacken. Typiske valuevariabler er de af .NET definerede typer som;

int
short
float
double
decimal
datetime
bool
samt structs
mm.

Valuevariabler indeholder værdier direkte. Sætter man to valuevariabler lig hinanden laves der en kopi, således at de ligger to steder på stacken.


Referencevariabler

Referencevariabler er klasser. Instanser / objekter af disse klasser gemmes på heapen. Flere referencevariabler kan pege på samme objekt. Dette medfører, at hvis en variabel ændrer værdi på objekt, slår denne ændring igennem på alle variabler der peger på objektet.
Referencetyper er alle de klasser, du selv opretter i et projekt, samt de fleste af de klasser .NET stiller til rådighed.

fredag den 10. maj 2013

Arbejde med strenge System.Text.StringBuilder

Strenge i c# er ikke value types, selvom de minder lidt om. De er reference types og gemmes på heapen (hukommelsen hvor reference types ligger). Streng er det man kalder immutable; dvs. de kan ikke ændres når de først ligger på heapen. 

System.String stiller en række metoder til rådighed for at manipulere strenge. Disse metoder kan fint anvendes hvis ressourceforbrug ikke er en bekymring. 

System.String bruger imidlertid en del ressourcer, da metoderne ikke arbejder med referencer men returnerer en ny streng i hukommelsen. Arbejder man således med en streng i et loop, kan det give rigtigt mange kopier af strenge, der skal oprettes i hukommelsen. Dette giver en del "Overhead" altså ressourceforbrug. 

En løsning på dette er at arbejde med StringBuilder Klassen, der findes i namespace System.Text. Ved at lave et StringBuilder objekt, kan man bruge metoder som Append og Remove på dette objekt uden at lave nye strenge i hukommelsen.

Eks 1.


        System.Text.StringBuilder sb = new StringBuilder();
        // Opretter et StringBuilder objekt sb

        sb.Append("Her er en streng");
        // Tilføjer streng til sb

        sb.Remove(0, 4);
        // Fjerner karakter i index 0-3 i sb streng

        sb.Replace("er", "Dette er");
        // Erstatter ordet "er" med "Dette er"

sb indeholder nu "Dette er en streng"

Ovenstående viser er et simpelt eksempel på brugen af metoderne Append, Remove og Replace. Istedet for System.Text.StringBuilder kan namespace System.Text selfølgelig importeres

using System.Text;

Der er flere overloadede versioner af metoderne Append, Remove ogg Replace, der gør, at man kan manipulere en streng helt efter ens eget hoved.