Indbyggede delegates
I de nyere versioner af C# vil du sjældent falde over brugen af delegate kodeordet, fordi der findes en bedre og nemmere måde at definere en delegate-type. Det sker ved hjælp af de indbyggede typer Action, Func og Predicate som findes i forskellige variationer.
Information til undervisere
Hvis først kursisterne har kendskab til delegates er det nemt at forklare Action, Func og Predicate. Det er jo bare den indbyggede generiske version af en delegate. Forklar at Predicate er en gammel type, der ikke bruges så meget mere (det er jo blot en Func med et argument og en returværdi af typen bool).
Type | Svarer til |
---|---|
Action | void delegate uden argumenter |
Action<T> | void delegate med argumenter (angivet som en liste af T) |
Func<T> | delegate med returværdi og eventuelle argumenter (angivet som en liste af T) |
Predicate<T> | delegate der altid returnerer en bool og tager et argument af typen T |
Du behøver ikke være så opmærksom på Predicate-typen, den benyttes ikke så meget mere i nyere C#, men ved at benytte de andre indbyggede delegates kan du slippe for at definere dine egne, så længe du holder dig til metoder med højst 15 argumenter.
Således kan de delegates, du så tidligere, helt undgås, fordi variabler kan erklæres således:
using System;
namespace Demo
{
internal class Program
{
// Brug af det gamle delegate-kodeord
public delegate void MinDelegate1();
public delegate void MinDelegate2(int a);
public delegate int MinDelegate3();
public delegate bool MinDelegate4(string a, int b);
private static void Main(string[] args)
{
MinDelegate1 d1a;
// Samme som
Action d1b;
MinDelegate2 d2a;
// Samme som
Action<int> d2b;
MinDelegate3 d3a;
// Samme som
Func<int> d3b;
MinDelegate4 d4a;
// Samme som
Func<string, int, bool> d4b;
}
}
}
Bemærk, at typen på eventuelle argumenter angives med den generiske definition <>, og når der er tale om en Func, svarer den sidste type til typen på returværdien. Når du benytter en Func så husk, at den sidste angivne type repræsenterer returværdien.
Det forrige eksempel på brug af en void delegate kan nu omskrives til brug af en Action:
using System;
namespace Demo
{
internal class Program
{
private static void Main(string[] args)
{
// Direkte afvikling
Metode1(); // I Metode1
// Inddirekte afvikling
Action d1 = Metode1;
d1.Invoke(); // I Metode1
// eller blot
d1(); // I Metode1
// Som argument
DelegateSomArgument(d1); // I Metode1
// Som returværdi
Action d2 =
DelegateSomReturVærdi();
d2(); // I Metode1
// eller blot
DelegateSomReturVærdi()(); // I Metode1
}
public static void Metode1()
{
Console.WriteLine("I Metode1");
}
public static void DelegateSomArgument(Action d)
{
d();
}
public static Action DelegateSomReturVærdi()
{
return Metode1;
}
}
}
Prøv at sammenligne koden med det forrige eksempel og du vil se, at delegate definitionen er væk, og typer er blot erstattet med en Action. Under alle omstændigheder ender det med et objekt af en delegate, som benyttes på samme måde. Det er bare meget nemmere at skrive Action, Action
using System;
namespace Demo
{
internal class Program
{
private static void Main(string[] args)
{
Func<int, int, int> d = LægSammen;
Console.WriteLine(d(5, 5)); // 10
}
public static int LægSammen(int a, int b)
{
return a + b;
}
}
}
kan du oversætte det til:
- Erklær en variabel d, som kan indeholde en reference til et delegate objekt, der kan indeholde referencer til metoder, der returnerer et heltal og tager to heltal som argumenter.
- Opret et nyt delegate objekt, hvor der gemmes en reference til metoden LægSammen og gem referencen i d
- Afvikling af den metode, der er gemt som reference i delegate objektet.
Flere referencer i en delegate
Som tidligere nævnt består et delegate objekt af en liste af referencer, og et kald til Invoke-metoden vil sørge for at afvikle dem alle. For at tilføje flere referencer kan du bruge += operatoren, og du kan fjerne referencer med -= operatoren. Det er best practice at fjerne referencer, når du er færdig med at bruge dem. Det kan ske ved enten at benytte -= operatoren eller ved at sætte variablen til null. I nogle situationer har det ingen betydning, men især i brugerflade-applikationer kan der være hængende referencer, og det er derfor et af de steder, hvor du kan komme til at skabe memory leaks – en af de sværeste fejl at finde i en C# applikation.
Her er et kort eksempel på tilføjelse af flere referencer:
using System;
namespace Demo
{
internal class Program
{
private static void Main(string[] args)
{
Action<string> d = Metode1;
d += Metode2;
d += Console.WriteLine;
d.Invoke("Test");
// eller bare d("Test");
/*
I Metode1 med Test
I Metode2 med Test
Test
*/
// Ryd op
d = null;
}
public static void Metode1(string a)
{
Console.WriteLine($"I Metode1 med {a}");
}
public static void Metode2(string a)
{
Console.WriteLine($"I Metode2 med {a}");
}
}
}
Som du kan se, tilføjes tre metoder til et nyt (Action