Gå til indholdet

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 eller Func. Så når du ser eller skriver kode som dette:

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) delegate objekt, og et kald til Invoke vil afvikle alle tre metoder på en gang. Hvis du ser eksemplet i starten af kapitlet, vil du se et andet eksempel på et delegate objekt med flere referencer.

Opgaver