Что такое Func, как и когда он используется - PullRequest
104 голосов
/ 02 сентября 2010

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

Ответы [ 7 ]

80 голосов
/ 02 сентября 2010

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

Например, рассмотрим метод расширения 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);

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

70 голосов
/ 02 сентября 2010

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

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

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

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

Func<string> f = GetMessage;
61 голосов
/ 02 сентября 2010

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

и т.д.

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

11 голосов
/ 16 мая 2017

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

Возьмите этот очень простой пример: компонент PrintListToConsole<T>.

Очень простой объект, который выводит этот список объектов на консоль.Вы хотите позволить разработчику, использующему его, персонализировать вывод.

Например, вы хотите, чтобы он определил определенный тип числового формата и т. Д.

Без функции

Сначала вы должны создать интерфейс для класса, который принимает входные данные и создает строку для печати на консоль.

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

11 голосов
/ 02 сентября 2010

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

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

6 голосов
/ 02 сентября 2010

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

0 голосов
/ 03 декабря 2018

Может быть, еще не поздно добавить информацию.

Сумма:

Func - это пользовательский делегат, определенный в пространстве имен System, который позволяет вам указывать на метод с такой же сигнатурой (как это делают делегаты), используя от 0 до 16 входных параметров, и который должен что-то возвращать.

Номенклатура и способ использования:

Func<input_1, input_2, ..., input1_6, output> funcDelegate = someMethod;

Определение:

public delegate TResult Func<in T, out TResult>(T arg);

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...