Что такое 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));
        }
    }   
}

Разработчик, которому необходимо использовать ваш компонент, должен:

  1. реализовать интерфейс

  2. передать реальный класс в 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);

Где он используется:

Он используется в лямбда-выражениях и анонимных методах.