Что такое функциональное, как и когда он используется


что это Func<> и для чего он используется?

6 87

6 ответов:

Func<T> является предопределенным типом делегата для метода, который возвращает некоторое значение типа T.

иными словами, вы можете использовать этот тип для ссылки на метод, который возвращает некоторое значение T. Е. Г.

public static string GetMessage() { return "Hello world"; }

можно ссылаться вот так

Func<string> f = GetMessage;

думайте об этом как о заполнителе. Это может быть очень полезно, когда у вас есть код, который следует определенному шаблону, но не должен быть привязан к какой-либо конкретной функциональности.

например,Enumerable.Select метод расширения.

  • The pattern is: для каждого элемента в последовательности выберите некоторое значение из этого элемента (например, свойство) и создайте новую последовательность, состоящую из этих значений.
  • The местозаполнитель is: некоторая функция селектора, которая фактически получает значения для последовательности, описанной выше.

этот метод принимает Func<T, TResult> вместо любой конкретной функции. Это позволяет использовать его в любой контекст, в котором применяется приведенный выше шаблон.

так, например, скажем, у меня есть List<Person> и я хочу только имя каждого человека в списке. Я могу это сделать:

var names = people.Select(p => p.Name);

или сказать, что я хочу возраст каждого персона:

var ages = people.Select(p => p.Age);

сразу же, вы можете видеть, как я был в состоянии использовать то же самое код, представляющий собой patternSelect) С разные функции (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);

С a делегат, выступающий в качестве заполнителя, я освобождаю себя от необходимости снова и снова выписывать один и тот же шаблон в таких случаях.

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)));

etc.

помните, что это делегат, для получения более подробной информации обратитесь к документации.

Func<T1,R> и другие предопределенные универсальные Func человек (Func<T1,T2,R>,Func<T1,T2,T3,R> и другие) являются универсальными делегатами, которые возвращают тип последнего универсального параметра.

если у вас есть функция, которая должна возвращать различные типы, в зависимости от параметров, вы можете использовать Func делегировать, указав тип возвращаемого значения.

Это просто предопределенный универсальный делегат. Используя его, вам не нужно объявлять каждый делегат. Существует еще один предопределенный делегат,Action<T, T2...>, что то же самое, но возвращает void.

найти Func<T> очень полезно, когда я создаю компонент, который должен быть персонализированным "на лету".

возьмите этот очень простой пример: a 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> позволяет определить универсальный интерфейс метода на лету. Вы определяете, какой тип ввода и какой тип вывода. Просто и лаконично.