Что такое Func<>
и для чего он используется?
Что такое Func, как и когда он используется
Ответ 1
Func<T>
- предопределенный тип делегата для метода, который возвращает некоторое значение типа T
.
Другими словами, вы можете использовать этот тип для ссылки на метод, возвращающий некоторое значение T
. Например.
public static string GetMessage() { return "Hello world"; }
может быть указан как
Func<string> f = GetMessage;
Ответ 2
Подумайте об этом как заполнитель. Это может быть очень полезно, когда у вас есть код, который следует за определенным шаблоном, но не обязательно должен привязываться к какой-либо конкретной функции.
Например, рассмотрим метод расширения 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);
С делегатом, действующим в качестве заполнителя, я освобождаюсь от необходимости повторять один и тот же шаблон снова и снова в таких случаях.
Ответ 3
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)));
и др.
Помните, что это делегат, для более подробной информации обратитесь к документации.
Ответ 4
Func<T1,R>
и другие предопределенные общие Func
делегаты (Func<T1,T2,R>
, Func<T1,T2,T3,R>
и другие) - это общие делегаты, которые возвращают тип последнего общего параметра.
Если у вас есть функция, которая должна возвращать разные типы, в зависимости от параметров вы можете использовать делегат Func
, указав тип возвращаемого значения.
Ответ 5
Я считаю 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 Array[1, 2, 3];
var printer = new PrintListToConsole<int>();
printer.SetRenderFunc((o) => "Number:" + o);
printer.Print();
string result = Console.ReadLine();
}
}
Func<T>
позволяет вам определять общий интерфейс метода "на лету". Вы определяете тип ввода и тип вывода. Простой и лаконичный.
Ответ 6
Это просто предопределенный общий делегат. Используя его, вам не нужно объявлять каждого делегата. Существует еще один предопределенный делегат, Action<T, T2...>
, который является тем же, но возвращает void.
Ответ 7
Может быть, еще не поздно добавить какую-то информацию.
Сумма:
Func - это пользовательский делегат, определенный в пространстве имен System, который позволяет указывать на метод с той же сигнатурой (как это делают делегаты), используя от 0 до 16 входных параметров и которые должны что-то возвращать.
Номенклатура и how2use:
Func<input_1, input_2, ..., input1_6, output> funcDelegate = someMethod;
Определение:
public delegate TResult Func<in T, out TResult>(T arg);
Где он используется:
Он используется в лямбда-выражениях и анонимных методах.