Что это такое Func<>
и для чего он нужен?
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.
Что это такое Func<>
и для чего он нужен?
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.
Ответы:
Func<T>
- это предопределенный тип делегата для метода, который возвращает некоторое значение типа T
.
Другими словами, вы можете использовать этот тип для ссылки на метод, который возвращает некоторое значение T
. Например
public static string GetMessage() { return "Hello world"; }
на это можно ссылаться
Func<string> f = GetMessage;
Func<T>
есть delegate TResult Func<out TResult>()
. Никаких аргументов. Func<T1, T2>
будет функцией, которая принимает один аргумент.
static int OneArgFunc(this string i) { return 42; }
Func<int> f = "foo".OneArgFunc;
. =)
Extension
атрибута - это атрибут, который читается только компиляторами C # / VB.Net, а не CLR. По сути, методы экземпляра (в отличие от статических функций) имеют скрытый 0-й параметр this. Итак, метод экземпляра с одним аргументом очень похож на статическую функцию с двумя аргументами. Затем у нас есть делегаты, которые хранят целевой объект и указатель на функцию . Делегаты могут хранить первый аргумент в target или не делать этого.
Думайте об этом как о заполнителе. Это может быть весьма полезно, если у вас есть код, который следует определенному шаблону, но не должен быть привязан к какой-либо конкретной функции.
Например, рассмотрим Enumerable.Select
метод расширения.
Этот метод принимает Func<T, TResult>
вместо какой-либо конкретной функции. Это позволяет использовать его в любом контексте, где применяется вышеуказанный шаблон.
Так, например, скажем, у меня есть List<Person>
и мне нужно только имя каждого человека в списке. Я могу это сделать:
var names = people.Select(p => p.Name);
Или скажите, что я хочу возраст каждого человека:
var ages = people.Select(p => p.Age);
Сразу видно, как мне удалось использовать один и тот же код, представляющий шаблон (с Select
) с двумя разными функциями ( p => p.Name
и p => p.Age
).
Альтернативой может быть написание новой версии Select
каждого случая, когда вы хотите сканировать последовательность для другого типа значения. Итак, чтобы добиться того же эффекта, что и выше, мне понадобятся:
// 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);
Когда делегат выступает в качестве заполнителя, я избавляюсь от необходимости повторять один и тот же шаблон снова и снова в подобных случаях.
Func<T1, T2, ..., Tn, Tr>
представляет функцию, которая принимает (T1, T2, ..., Tn) аргументов и возвращает Tr.
Например, если у вас есть функция:
double sqr(double x) { return x * x; }
Вы можете сохранить его как некую функцию-переменную:
Func<double, double> f1 = sqr;
Func<double, double> f2 = x => x * x;
А затем используйте точно так же, как если бы вы использовали sqr:
f1(2);
Console.WriteLine(f2(f1(4)));
и т.п.
Однако помните, что это делегат, для получения более подробной информации обратитесь к документации.
Я считаю Func<T>
очень полезным, когда создаю компонент, который нужно персонализировать «на лету».
Возьмем этот очень простой пример: PrintListToConsole<T>
компонент.
Очень простой объект, который выводит этот список объектов на консоль. Вы хотите позволить разработчику, который его использует, персонализировать вывод.
Например, вы хотите позволить ему определять определенный тип числового формата и так далее.
Без Func
Во-первых, вы должны создать интерфейс для класса, который принимает входные данные и создает строку для вывода на консоль.
interface PrintListConsoleRender<T> {
String Render(T input);
}
Затем вам нужно создать класс, PrintListToConsole<T>
который берет ранее созданный интерфейс и использует его для каждого элемента списка.
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));
}
}
}
Разработчик, которому необходимо использовать ваш компонент, должен:
реализовать интерфейс
передать настоящий класс 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();
}
}
Используя Func, это намного проще
Внутри компонента вы определяете параметр типа, Func<T,String>
который представляет интерфейс функции, которая принимает входной параметр типа T и возвращает строку (вывод для консоли)
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));
}
}
}
Когда разработчик использует ваш компонент, он просто передает компоненту реализацию Func<T, String>
типа, то есть функцию, которая создает вывод для консоли.
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>
позволяет определять интерфейс универсального метода на лету.
Вы определяете, к какому типу относятся входные и выходные данные. Просто и лаконично.
Func<T1,R>
и другие предопределенные общие Func
делегаты ( Func<T1,T2,R>
, Func<T1,T2,T3,R>
и другие) являются общими делегатами , которые возвращают тип последнего родового параметра.
Если у вас есть функция, которая должна возвращать разные типы в зависимости от параметров, вы можете использовать Func
делегат, указав тип возвращаемого значения.
Это просто предопределенный универсальный делегат. Используя его, вам не нужно объявлять каждого делегата. Есть еще один предопределенный делегат, Action<T, T2...>
который такой же, но возвращает void.
Может, еще не поздно добавить инфу.
Сумма:
Func - это настраиваемый делегат, определенный в пространстве имен System, который позволяет вам указывать на метод с той же сигнатурой (что и делегаты), используя от 0 до 16 входных параметров, которые должны что-то возвращать.
Номенклатура и способы использования:
Func<input_1, input_2, ..., input1_6, output> funcDelegate = someMethod;
Определение:
public delegate TResult Func<in T, out TResult>(T arg);
Где используется:
Он используется в лямбда-выражениях и анонимных методах.