Cos'è Func<>
e a cosa serve?
If you have a function that needs to return different types, depending on the parameters, you can use a Func delegate, specifying the return type.
Cos'è Func<>
e a cosa serve?
If you have a function that needs to return different types, depending on the parameters, you can use a Func delegate, specifying the return type.
Risposte:
Func<T>
è un tipo di delegato predefinito per un metodo che restituisce un valore del tipo T
.
In altre parole, puoi usare questo tipo per fare riferimento a un metodo che restituisce un valore di T
. Per esempio
public static string GetMessage() { return "Hello world"; }
può essere referenziato in questo modo
Func<string> f = GetMessage;
Func<T>
is delegate TResult Func<out TResult>()
. Nessun argomento. Func<T1, T2>
sarebbe una funzione che accetta un argomento.
static int OneArgFunc(this string i) { return 42; }
Func<int> f = "foo".OneArgFunc;
. =)
Extension
attributo che viene letto solo dai compilatori C # / VB.Net, non CLR. Fondamentalmente, i metodi di istanza (a differenza delle funzioni statiche) hanno un parametro 0th nascosto "this". Quindi, il metodo di istanza a 1 argomento è molto simile alla funzione statica a 2 argomenti. Quindi, abbiamo delegati che memorizzano l' oggetto di destinazione e il puntatore a funzione . I delegati possono memorizzare il primo argomento in target o non farlo.
Consideralo come un segnaposto. Può essere molto utile quando si dispone di codice che segue un certo schema ma non è necessario che sia legato a nessuna funzionalità particolare.
Ad esempio, considera il Enumerable.Select
metodo di estensione.
Questo metodo accetta una funzione Func<T, TResult>
anziché qualsiasi funzione concreta. Ciò consente di utilizzarlo in qualsiasi contesto in cui si applica il modello sopra.
Quindi, ad esempio, diciamo che ho un List<Person>
e voglio solo il nome di ogni persona nell'elenco. Posso farlo:
var names = people.Select(p => p.Name);
Oppure dì che voglio l' età di ogni persona:
var ages = people.Select(p => p.Age);
Immediatamente, puoi vedere come sono stato in grado di sfruttare lo stesso codice che rappresenta un pattern (con Select
) con due diverse funzioni ( p => p.Name
e p => p.Age
).
L'alternativa sarebbe scrivere una versione diversa Select
ogni volta che si desidera scansionare una sequenza per un diverso tipo di valore. Quindi, per ottenere lo stesso effetto di cui sopra, avrei bisogno di:
// Presumably, the code inside these two methods would look almost identical;
// the only difference would be the part that actually selects a value
// based on a Person.
var names = GetPersonNames(people);
var ages = GetPersonAges(people);
Con un delegato che funge da segnaposto, mi libero di dover scrivere lo stesso schema più e più volte in casi come questo.
Func<T1, T2, ..., Tn, Tr>
rappresenta una funzione, che accetta argomenti (T1, T2, ..., Tn) e restituisce Tr.
Ad esempio, se hai una funzione:
double sqr(double x) { return x * x; }
Puoi salvarlo come una sorta di variabile di funzione:
Func<double, double> f1 = sqr;
Func<double, double> f2 = x => x * x;
E poi usa esattamente come useresti sqr:
f1(2);
Console.WriteLine(f2(f1(4)));
eccetera.
Ricorda però che è un delegato, per informazioni più avanzate fare riferimento alla documentazione.
Trovo Func<T>
molto utile quando creo un componente che necessita di essere personalizzato "al volo".
Prendi questo esempio molto semplice: un PrintListToConsole<T>
componente.
Un oggetto molto semplice che stampa questo elenco di oggetti sulla console. Vuoi consentire allo sviluppatore che lo utilizza di personalizzare l'output.
Ad esempio, vuoi lasciargli definire un particolare tipo di formato numerico e così via.
Senza Func
Per prima cosa, devi creare un'interfaccia per una classe che prende l'input e produce la stringa da stampare sulla console.
interface PrintListConsoleRender<T> {
String Render(T input);
}
Quindi devi creare la classe PrintListToConsole<T>
che prende l'interfaccia creata in precedenza e la usa su ogni elemento della lista.
class PrintListToConsole<T> {
private PrintListConsoleRender<T> _renderer;
public void SetRenderer(PrintListConsoleRender<T> r) {
// this is the point where I can personalize the render mechanism
_renderer = r;
}
public void PrintToConsole(List<T> list) {
foreach (var item in list) {
Console.Write(_renderer.Render(item));
}
}
}
Lo sviluppatore che deve utilizzare il tuo componente deve:
implementare l'interfaccia
passare la vera classe al PrintListToConsole
class MyRenderer : PrintListConsoleRender<int> {
public String Render(int input) {
return "Number: " + input;
}
}
class Program {
static void Main(string[] args) {
var list = new List<int> { 1, 2, 3 };
var printer = new PrintListToConsole<int>();
printer.SetRenderer(new MyRenderer());
printer.PrintToConsole(list);
string result = Console.ReadLine();
}
}
Usare Func è molto più semplice
All'interno del componente si definisce un parametro di tipo Func<T,String>
che rappresenta un'interfaccia di una funzione che accetta un parametro di input di tipo T e restituisce una stringa (l'output per la console)
class PrintListToConsole<T> {
private Func<T, String> _renderFunc;
public void SetRenderFunc(Func<T, String> r) {
// this is the point where I can set the render mechanism
_renderFunc = r;
}
public void Print(List<T> list) {
foreach (var item in list) {
Console.Write(_renderFunc(item));
}
}
}
Quando lo sviluppatore utilizza il tuo componente passa semplicemente al componente l'implementazione del Func<T, String>
tipo, cioè una funzione che crea l'output per la console.
class Program {
static void Main(string[] args) {
var list = new List<int> { 1, 2, 3 }; // should be a list as the method signature expects
var printer = new PrintListToConsole<int>();
printer.SetRenderFunc((o) => "Number:" + o);
printer.Print(list);
string result = Console.ReadLine();
}
}
Func<T>
consente di definire al volo un'interfaccia di metodo generico.
Definisci il tipo di input e il tipo di output. Semplice e conciso.
Func<T1,R>
e gli altri predefiniti generici Func
delegati ( Func<T1,T2,R>
, Func<T1,T2,T3,R>
e altri) sono delegati generici che restituiscono il tipo dell'ultimo parametro generico.
Se si dispone di una funzione che deve restituire tipi diversi, a seconda dei parametri, è possibile utilizzare un Func
delegato, specificando il tipo restituito.
È solo un delegato generico predefinito. Usandolo non è necessario dichiarare ogni delegato. C'è un altro delegato predefinito Action<T, T2...>
, che è lo stesso ma restituisce void.
Forse non è troppo tardi per aggiungere alcune informazioni.
Somma:
Il Func è un delegato personalizzato definito nello spazio dei nomi di sistema che consente di puntare a un metodo con la stessa firma (come fanno i delegati), utilizzando da 0 a 16 parametri di input e che deve restituire qualcosa.
Nomenclatura e come2uso:
Func<input_1, input_2, ..., input1_6, output> funcDelegate = someMethod;
Definizione:
public delegate TResult Func<in T, out TResult>(T arg);
Dove viene utilizzato:
Viene utilizzato nelle espressioni lambda e nei metodi anonimi.