Delegates
Du kan opfatte en delegate som en form for en autogenereret klasse.
Information til undervisere
Selv om man ikke i C# bruger den “rå” delegate så meget mere (nu bruges Action/Func) er det vigtigt at forstå hvordan den fungerer. Brug figuren nedenfor til at forklare hvordan en delegate fungerer - et objekt med en liste af referencer til metoder med samme signatur! Lad være med at snakke om funktionspointere - det er en anden snak og kan forvirre mere end det gavner - især i C#. I C# er der ikke “pointere” i traditionel forstand - der er kun referencer.
Den repræsenterer en skabelon, du kan skabe instanser af, men i modsætning til klasser, hvor du kan definere mange forskellige medlemmer (felter, egenskaber, metoder med videre), så kan du som udgangspunkt kun definere én ting i en delegate – nemlig hvilken metodesignatur (returværdi og argumenter) instanser af typen kan indeholde referencer til. Klassen kan du efterfølgende lave en instans af, og objektet indholder blandt en liste af referencer til metoder (af samme signatur) samt metoder til at afvikle metoderne.
Du kan arbejde med delegates på to måder. I ældre C# kode vil du måske falde over en definition som eksempelvis:
public delegate void MinDelegate1();
public delegate void MinDelegate2(int a);
public delegate int MinDelegate3();
public delegate bool MinDelegate4(string a, int b);
Koden definerer fire typer hvor: - MinDelegate1 kan indeholde referencer til metoder uden returværdi og uden argumenter - MinDelegate2 kan indeholde referencer til metoder uden returværdi og et enkelt int argument - MinDelegate3 kan indeholde referencer til metoder, der returnerer en int uden argumenter - MinDelegate3 kan indeholde referencer til metoder, der returnerer en bool med argumenterne string og int.
Definition er typisk placeret på namespace niveau ligesom klasser og kan benyttes på samme måde, som klasser benyttes til at skabe instanser.
Dog kan instanser oprettes på flere måder:
using System;
namespace Demo
{
public delegate void MinDelegate1();
internal class Program
{
private static void Main(string[] args)
{
// Brug af konstruktør
MinDelegate1 d1 = new MinDelegate1(Metode1);
// Genvej til oprettelse af instans (blot ref til metode)
MinDelegate1 d2 = Metode1;
}
public static void Metode1()
{
Console.WriteLine("I Metode1");
}
}
}
Når først instanser af en delegate type er oprettet, kan de metoder, der er gemt referencer til, afvikles – igen på flere måder:
using System;
namespace Demo
{
public delegate void MinDelegate1();
internal class Program
{
private static void Main(string[] args)
{
// Direkte afvikling
Metode1(); // I Metode1
// Inddirekte afvikling
MinDelegate1 d1 = Metode1;
d1.Invoke(); // I Metode1
// eller blot
d1(); // I Metode1
// Som argument
DelegateSomArgument(d1); // I Metode1
// Som returværdi
MinDelegate1 d2 = DelegateSomReturVærdi();
d2(); // I Metode1
// eller blot
DelegateSomReturVærdi()(); // I Metode1
}
public static void Metode1()
{
Console.WriteLine("I Metode1");
}
public static void DelegateSomArgument(MinDelegate1 d)
{
d();
}
public static MinDelegate1 DelegateSomReturVærdi()
{
return Metode1;
}
}
}
Bemærk, at referencer til metoder gemt i en instans af en delegate kan afvikles på flere måder – eksempelvis:
eller blot:
Det er ligegyldigt, hvilken version du vælger, men normalt bør du teste om et delegate-objekt indeholder referencer, hvilket (lidt ulogisk) kan gøres ved at teste om objektet er null:
Referencen til delegate-instansen kan naturligvis benyttes til både returværdi og argument, ligesom den kan gemmes i objekter.
Warning
Husk at kontrollere for en null værdi inden du kalder Invoke-metoden.
Eksemplet benytter udelukkende en delegate, der kan indeholde referencer til en void-metode uden argumenter, men prøv at kopiere koden til en ny konsolapplikation og leg lidt med at ændre typerne til at håndtere forskellige returværdier og argumenter.