Co to jest Func<>
i do czego służy?
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.
Co to jest Func<>
i do czego służy?
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.
Odpowiedzi:
Func<T>
jest predefiniowanym typem delegata dla metody, która zwraca pewną wartość typu T
.
Innymi słowy, możesz użyć tego typu, aby odwołać się do metody, która zwraca pewną wartość T
. Na przykład
public static string GetMessage() { return "Hello world"; }
mogą być określane w ten sposób
Func<string> f = GetMessage;
Func<T>
jest delegate TResult Func<out TResult>()
. Żadnych argumentów.Func<T1, T2>
byłaby funkcją, która przyjmuje jeden argument.
static int OneArgFunc(this string i) { return 42; }
Func<int> f = "foo".OneArgFunc;
. =)
Extension
atrybut, który jest odczytywany tylko przez kompilatory C # / VB.Net, a nie CLR. Zasadniczo metody instancji (w przeciwieństwie do funkcji statycznych) mają ukryty zerowy parametr „ten”. Zatem metoda instancji z 1 argumentem jest bardzo podobna do funkcji statycznej z 2 argumentami. Następnie mamy delegatów, którzy przechowują obiekt docelowy i wskaźnik funkcji . Delegaci mogą przechowywać pierwszy argument w miejscu docelowym lub tego nie robić.
Potraktuj to jako symbol zastępczy. Może to być całkiem przydatne, gdy masz kod, który jest zgodny z określonym wzorcem, ale nie musi być powiązany z żadną konkretną funkcjonalnością.
Weźmy na przykład pod uwagę Enumerable.Select
metodę rozszerzenia.
Ta metoda przyjmuje Func<T, TResult>
zamiast dowolnej konkretnej funkcji. Pozwala to na użycie go w dowolnym kontekście, w którym ma zastosowanie powyższy wzorzec.
Na przykład, powiedzmy, że mam plik List<Person>
i chcę tylko nazwiska każdej osoby na liście. Mogę to zrobić:
var names = people.Select(p => p.Name);
Albo powiedz, że chcę wiek każdej osoby:
var ages = people.Select(p => p.Age);
Od razu widać, jak udało mi się wykorzystać ten sam kod reprezentujący wzorzec (z Select
) z dwiema różnymi funkcjami ( p => p.Name
ip => p.Age
).
Alternatywą byłoby zapisanie innej wersji za Select
każdym razem, gdy chciałbyś przeskanować sekwencję pod kątem innego rodzaju wartości. Aby osiągnąć taki sam efekt jak powyżej, potrzebowałbym:
// 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);
Dzięki delegatowi pełniącemu rolę elementu zastępczego, uwalniam się od konieczności ciągłego pisania tego samego wzoru w takich przypadkach.
Func<T1, T2, ..., Tn, Tr>
reprezentuje funkcję, która przyjmuje (T1, T2, ..., Tn) argumenty i zwraca Tr.
Na przykład, jeśli masz funkcję:
double sqr(double x) { return x * x; }
Możesz zapisać to jako rodzaj zmiennej funkcji:
Func<double, double> f1 = sqr;
Func<double, double> f2 = x => x * x;
Następnie użyj dokładnie tak, jak używałbyś sqr:
f1(2);
Console.WriteLine(f2(f1(4)));
itp.
Pamiętaj jednak, że jest to delegat, więcej szczegółowych informacji znajdziesz w dokumentacji.
znajduję Func<T>
bardzo przydatne podczas tworzenia komponentu, który wymaga personalizacji „w locie”.
Weźmy ten bardzo prosty przykład: a PrintListToConsole<T>
komponent.
Bardzo prosty obiekt, który drukuje listę obiektów na konsoli. Chcesz, aby deweloper, który go używa, spersonalizował dane wyjściowe.
Na przykład chcesz pozwolić mu zdefiniować określony typ formatu liczb i tak dalej.
Bez Func
Najpierw musisz utworzyć interfejs dla klasy, która pobiera dane wejściowe i tworzy ciąg do wydrukowania na konsoli.
interface PrintListConsoleRender<T> {
String Render(T input);
}
Następnie musisz utworzyć klasę, PrintListToConsole<T>
która przejmuje poprzednio utworzony interfejs i używa go do każdego elementu listy.
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));
}
}
}
Deweloper, który musi użyć Twojego komponentu, musi:
zaimplementować interfejs
przekazać prawdziwą klasę do 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();
}
}
Korzystanie z Func jest znacznie prostsze
Wewnątrz komponentu definiujesz parametr typu, Func<T,String>
który reprezentuje interfejs funkcji, która przyjmuje parametr wejściowy typu T i zwraca ciąg (dane wyjściowe dla konsoli)
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));
}
}
}
Gdy programista używa twojego komponentu, po prostu przekazuje do komponentu implementację Func<T, String>
typu, czyli funkcji, która tworzy dane wyjściowe dla konsoli.
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>
pozwala zdefiniować ogólny interfejs metody w locie.
Definiujesz, jakiego typu jest wejście i jaki jest typ wyjścia. Proste i zwięzłe.
Func<T1,R>
i inne predefiniowane generyczne Func
delegaci ( Func<T1,T2,R>
, Func<T1,T2,T3,R>
i inne) są generyczne delegaci które zwracają typ ostatniego parametru rodzajowego.
Jeśli masz funkcję, która musi zwracać różne typy, w zależności od parametrów, możesz użyć Func
delegata, określając zwracany typ.
Jest to po prostu wstępnie zdefiniowany delegat ogólny. Używając go, nie musisz deklarować każdego delegata. Istnieje inny predefiniowany delegat, Action<T, T2...>
który jest taki sam, ale zwraca void.
Może nie jest za późno, aby dodać informacje.
Suma:
Func to niestandardowy delegat zdefiniowany w przestrzeni nazw System, który umożliwia wskazanie metody z tym samym podpisem (co robią delegaci), używając od 0 do 16 parametrów wejściowych i która musi coś zwrócić.
Nazewnictwo i sposób użycia:
Func<input_1, input_2, ..., input1_6, output> funcDelegate = someMethod;
Definicja:
public delegate TResult Func<in T, out TResult>(T arg);
Gdzie jest używany:
Jest używany w wyrażeniach lambda i metodach anonimowych.