Starkt skrivna ombud
I föregående artikel såg du att du skapar specifika ombudstyper med hjälp av nyckelordet delegate
.
Den abstrakta delegatklassen tillhandahåller infrastrukturen för lös koppling och anrop. Konkreta ombudstyper blir mycket mer användbara genom att använda och framtvinga typsäkerhet för de metoder som läggs till i listan över anrop för ett ombudsobjekt. När du använder nyckelordet delegate
och definierar en konkret delegattyp genererar kompilatorn dessa metoder.
I praktiken skulle detta leda till att du skapar nya ombudstyper när du behöver en annan metodsignatur. Det här arbetet kan bli omständligt efter en tid. Varje ny funktion kräver nya ombudstyper.
Tack och lov är detta inte nödvändigt. .NET Core-ramverket innehåller flera typer som du kan återanvända när du behöver ombudstyper. Det här är allmänna definitioner så att du kan deklarera anpassningar när du behöver nya metoddeklarationer.
Den första av dessa typer är Action typen och flera varianter:
public delegate void Action();
public delegate void Action<in T>(T arg);
public delegate void Action<in T1, in T2>(T1 arg1, T2 arg2);
// Other variations removed for brevity.
Modifieraren in
för argumentet generisk typ beskrivs i artikeln om kovarians.
Det finns varianter av ombudet Action
som innehåller upp till 16 argument, till exempel Action<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16>.
Det är viktigt att dessa definitioner använder olika allmänna argument för vart och ett av de delegerade argumenten: Det ger dig maximal flexibilitet. Metodargumenten behöver inte vara, men de kan vara av samma typ.
Använd en av typerna Action
för alla ombudstyper som har en ogiltig returtyp.
Ramverket innehåller också flera allmänna ombudstyper som du kan använda för ombudstyper som returnerar värden:
public delegate TResult Func<out TResult>();
public delegate TResult Func<in T1, out TResult>(T1 arg);
public delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2);
// Other variations removed for brevity
Modifieraren out
för argumentet för den generiska resultattypen beskrivs i artikeln om kovarians.
Det finns varianter av ombudet Func
med upp till 16 indataargument, till exempel Func<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16,TResult>.
Typen av resultat är alltid den sista typparametern i alla Func
deklarationer, enligt konvention.
Använd en av typerna Func
för valfri ombudstyp som returnerar ett värde.
Det finns också en särskild Predicate<T> typ för ett ombud som returnerar ett test på ett enda värde:
public delegate bool Predicate<in T>(T obj);
Du kanske märker att det för alla Predicate
typer finns en strukturellt likvärdig Func
typ, till exempel:
Func<string, bool> TestForString;
Predicate<string> AnotherTestForString;
Du kanske tror att dessa två typer är likvärdiga. Det sker inte. Dessa två variabler kan inte användas omväxlande. En variabel av en typ kan inte tilldelas den andra typen. C#-typsystemet använder namnen på de definierade typerna, inte strukturen.
Alla dessa definitioner av ombudstyp i .NET Core-biblioteket bör innebära att du inte behöver definiera en ny ombudstyp för någon ny funktion som du skapar som kräver ombud. Dessa allmänna definitioner bör tillhandahålla alla de ombudstyper som du behöver i de flesta situationer. Du kan helt enkelt instansiera en av dessa typer med de obligatoriska typparametrarna. När det gäller algoritmer som kan göras generiska kan dessa ombud användas som generiska typer.
Detta bör spara tid och minimera antalet nya typer som du behöver skapa för att arbeta med ombud.
I nästa artikel visas flera vanliga mönster för att arbeta med ombud i praktiken.