Delegaatti (delegate) on tyyppi, joka edustaa viittausta (reference) joukkoon metodeja tietyllä etukäteen määritelyillä parametreillä ja paluuarvolla. Delegaatteja käytetään välittämään metodeja argumentteina toisille metodeille. Kun delegaatti on alustettu, siihen voidaan kiinnittää mikä tahansa metodi, joka on yhteensopiva metodikutsun parametrien ja paluuarvon kanssa. Delegaattiin kiinnitetyt metodit suoritetaan delegaatin instanssilla. Delegaatit ovat siis osoittimia metodeihin, eli delegaattia kutsuttaessa kutsutaankin sitä kuuntelevia metodeja. Delegaatti voi osoittaa useampaan metodiin yhtä aikaa.
Tapahtumankäsittelijät (event handlers) ovat itseasiassa metodeja, joita kutsutaan delegaattien avulla. Esimerkiksi GUI-ohjelmoinnissa voit luoda haluamasi metodin, jota kutsutaan kun tietty tapahtuma (event) vaikkapa Click tapahtuu käyttöliittymäkontrollille.
Seuraavat kolme koodiriviä kertovat millä tavoin metodille voidaan välittää argumentteina muuttujia tai metodeja siis delegaatteja.
// kokonaisluvun 5 välittäminen DoSomething-metodille
DoSomething( 5 );
// viittauksen välittäminen uuteen Person-olioon DoSomething-metodille
DoSomething( new Person() );
// metodin laskeAlennus välittäminen DoSomething-metodille
DoSomething( laskeAlennus );
Delegaatin esittely on kuten metodin esittely ilman toteutusta (abstrakti metodi) lisättynä delegate-avainsanalla. Seuraavalla koodesimerkissä on määritelty delegaatti ja sitten siitä on tehty instanssi muuttujaan peca.
class MyClass {
// define a delegate
public delegate int PerformCalculation(int x, int y);
// later in method - use delegate
PerformCalculation peca = new PerformCalculation;
}
Delegaateilla on seuraavia ominaisuuksia:
- Delegaatit ovat kuin C++ funktiopointterit, mutta ne ovat tyyppiturvallisia.
- Delegaattien avulla voidaan välittää metodeille metodeja samalla tavalla kuin metodeille välitetään parametrejä.
- Delegaatteja voidaan "niputtaa", eli esimerkiksi yksi tapahtuma voi kutsua useita metodeja.
- Delegaattien avulla voidaan määrittele takaisinkutsu (callback)-metodeja.
- Vaikka pääsääntöisesti delegaattien metodien täytyy vastata määriteltyä tyyppiä niin .NET Framework esitteli mahdollisuuden tyyppivariaatioon, kts lisää Using Variance in Delegates.
Allaolevassa koodissa on määritelty delegaatti CalcHandler, ja siitä luotu delegaatti sumHandler. Sille on kiinnitetty ensin metodi: MyMath.Sum ja sitten MyMath.FakeSum. Delegaatti suoritetaan vuorotellen kummallakin metodilla.
Allaolevassa koodissa on ensin määritelty delegaatti FormatoiLuku, ja siitä luotu instanssi formatoija. Sille on kiinnitetty kaksi eri metodia: FormatoiValuutaksi ja FormatoiKolmelleDesimaalille. Tämän jälkeen formatoija-instanssille annetaan käyttäjän antama luku, ja delegaatti-instanssi suorittaa kaikki siihen liitetyt metodit.
Action-delegaattia käytetään metodin välittämiseen parametrina luomattaa erikseen delegaatti-instanssia. Delegaattiin liitettävän metodin parametrien ja paluuarvon on vastattava Action-delegaatin määrittelyä.
Seuraavassa esimerkissä luodaan Action-tyyppi, johon kiinnitetään kaksi eri metodia. Action-tyyppiin kiinnitetty metodi suoriteaan listalle nimiä.
namespace JAMK.IT {
public class DemoAction
{
public static void Test_Action()
{
List names = new List() { "Arska", "Pena", "Camilla" };
//use the method SanoHei (in Finnish)
Action myaction = new Action(Hello.SanoHei);
names.ForEach(myaction);
//use the method SayHello (in English)
myaction = new Action(Hello.SayHello);
names.ForEach(myaction);
}
}
public class Hello
{
public static void SayHello(string name)
{
Console.WriteLine($"Hello {name}, nice to see you.");
}
public static void SanoHei(string name)
{
Console.WriteLine($"Hei, mitä kuuluu {name}?");
}
}
}
C#-kielen versiossa 2.0 esiteltiin konsepti anonyymit metodit (anonymous methods), joka mahdollistaa metodin määrittelyn suoraan parametrikutsussa ilman että erikseen määritellään ja nimetään metodi. C#-kielen 3.0-versiossa esiteltiin lambda expressions joka on fiksumpi tapa määrittää metodeja delegaateille "lennosta". Sekä anonyymit metodit että lambda expressions käännetään delegaattityypiksi. Yhdessä, nämä ominaisuudet tunnetaan käsitteenä anonyymit funktiot (anonymous functions).
Lambda-lauseke on lyhennetty tapa esittää anonyymi metodi.
Lambda-lauseke koostuu kolmesta osasta:
- vasemmalla ovat parametrit
keskellä lambda-operaattori (=>)
oikealla parametreille suoritettava lauseke.
Parametreja voi olla kuinka monta tahansa tai niitä ei välttämättä ole yhtään.
Jos parametreja on useita, ne tulee laittaa sulkuihin
(x, y) => x + y
Jos vain yksi parametri, niin sulut voidaan jättää poisx => x + 1
Lambda-lausekkeita käytetään esim, LINQ-kyselyissä
List nums = new List { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
//haetaan parilliset
List evens = nums.Where(n => n % 2 == 0).ToList();
Seuraava koodi näyttää kaikki listan nimet konsolilla
//four names to List
List nimet = new List { "Arska", "Kalle", "Cecilia", "Jack" };
nimet.ForEach(x => Console.WriteLine(x)); //x is a variable inside lambda
Oheinen koodinpätkässä haetaan lambda-kutsulla nimistä ensimmäinen Ja-alkuinen nimi.
//four names to List
List nimet = new List { "Arska", "Kalle", "Cecilia", "Jack" };
string foo = nimet.FirstOrDefault(x => x.StartsWith("Ja"));
Console.WriteLine(foo); //this prints Jack
Seuraavassa demossa haetaan lambda-kutsulla ensimmäinen Inemo-olio jonka nimi alkaa Ja:lla.
public class TestLambda
{
public class Inemo { public string Nimi { get; set; } }
public static void TestInemoSearch()
{
//four Inemos to List
List immeiset = new List { new Inemo { Nimi = "Arska Kalinen" },
new Inemo { Nimi = "Jack Russell" },
new Inemo { Nimi = "Cecila Camilsson" },
new Inemo { Nimi = "Teppo Testaaja" } };
//try to find Inemo whcih names start with Ja, if found it returns an Inemo object
var foo = immeiset.FirstOrDefault(x => x.Nimi.StartsWith("Ja"));
if (foo != null)
{
Inemo inemo = foo;
Console.WriteLine(("Found Inemo with name:" + inemo.Nimi)); //this prints Jack Russell
}
}
Jos haluat lukea lisää niistä, niin katso: Anonymous Functions in C# Programming Guide. Tulet tutustumaan niihin myöhemmin varsinkin LINQ:n yhteydessä.
C#-kieli tarjoaa delegaatit callback-kutsujen tekemistä varten ja vähän muutakin varten. Delegaatit ovat C#:n (ja yleisemmin .NET Frameworkin) tapa välittää metodeja niin kuin parametrit välittävät tietoa eli dataa.
Delegates
Anonymous Functions
Lambda Expressions