ti-enxame.com

O que é Func, como e quando é usado

O que é Func<> e para que é usado?

102
learning

Func<T> é um tipo de delegado predefinido para um método que retorna algum valor do tipo T.

Em outras palavras, você pode usar esse tipo para referenciar um método que retorna algum valor de T. Por exemplo.

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

pode ser referenciado assim

Func<string> f = GetMessage;
69
Brian Rasmussen

Pense nisso como um espaço reservado. Pode ser bastante útil quando você tem um código que segue um determinado padrão, mas não precisa estar vinculado a nenhuma funcionalidade específica.

Por exemplo, considere o método de extensão Enumerable.Select.

  • O padrão é: para cada item em uma sequência, selecione algum valor desse item (por exemplo, uma propriedade) e crie uma nova sequência que consista nesses valores.
  • O espaço reservado é: alguma função de seleção que realmente obtém os valores para a seqüência descrita acima.

Este método leva um Func<T, TResult> em vez de qualquer função concreta. Isso permite que ele seja usado em qualquer contexto onde o padrão acima se aplica.

Por exemplo, digamos que eu tenha um List<Person> e quero apenas o nome de cada pessoa na lista. Eu posso fazer isso:

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

Ou diga que eu quero o idade de cada pessoa:

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

Logo, você pode ver como eu era capaz de alavancar o código mesmo representando um padrão (com Select) com duas diferentes funções (p => p.Name e p => p.Age).

A alternativa seria escrever uma versão diferente de Select toda vez que você quisesse digitalizar uma sequência para um tipo diferente de valor. Então, para conseguir o mesmo efeito acima, eu precisaria:

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

Com um delegado agindo como espaço reservado, eu me liberto de ter que escrever o mesmo padrão repetidamente em casos como este.

78
Dan Tao

Func<T1, T2, ..., Tn, Tr> representa uma função que recebe argumentos (T1, T2, ..., Tn) e retorna Tr.

Por exemplo, se você tiver uma função:

double sqr(double x) { return x * x; }

Você pode salvá-lo como algum tipo de variável de função:

Func<double, double> f1 = sqr;
Func<double, double> f2 = x => x * x;

E então use exatamente como você usaria sqr:

f1(2);
Console.WriteLine(f2(f1(4)));

etc.

Lembre-se, porém, que é um delegado, para informações mais avançadas, consulte a documentação.

60
Grozz

Func<T1,R> e os outros delegados Func genéricos predefinidos (Func<T1,T2,R>, Func<T1,T2,T3,R> e outros) são delegados genéricos que retornam o tipo do último parâmetro genérico.

Se você tiver uma função que precise retornar tipos diferentes, dependendo dos parâmetros, poderá usar um delegado Func, especificando o tipo de retorno.

11
Oded

Eu acho Func<T> muito útil quando eu crio um componente que precisa ser personalizado "on the fly".

Tome este exemplo muito simples: um componente PrintListToConsole<T>.

Um objeto muito simples que imprime esta lista de objetos para o console. Você deseja permitir que o desenvolvedor que o usa personalize a saída.

Por exemplo, você quer que ele defina um tipo específico de formato numérico e assim por diante.

sem Func

Primeiro, você precisa criar uma interface para uma classe que recebe a entrada e produz a string para imprimir no console.

interface PrintListConsoleRender<T> {
  String Render(T input);
}

Então você tem que criar a classe PrintListToConsole<T> que pega a interface criada anteriormente e a usa sobre cada elemento da lista.

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

O desenvolvedor que precisa usar seu componente deve:

  1. implementar a interface

  2. passar a classe real para o 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();   
        }   
    }
    

Usando Func é muito mais simples

Dentro do componente você define um parâmetro do tipo Func<T,String> que representa uma interface de uma função que recebe um parâmetro de entrada do tipo T e retorna uma string (a saída para o console)

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

Quando o desenvolvedor usa seu componente, ele simplesmente passa para o componente a implementação do tipo Func<T, String>, que é uma função que cria a saída para o console.

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> permite que você defina uma interface de método genérico na hora Você define o tipo de entrada e o tipo de saída. Simples e conciso.

10
Marco Staffoli

É apenas um delegado genérico predefinido. Ao usá-lo, você não precisa declarar todos os delegados. Há outro delegado predefinido, Action<T, T2...>, que é o mesmo, mas retorna nulo.

6
Stefan Steinegger

Talvez não seja tarde demais para adicionar alguma informação.

soma:

O Func é um delegado personalizado definido no namespace System que permite apontar para um método com a mesma assinatura (como fazem os delegados), usando de 0 a 16 parâmetros de entrada e que devem retornar alguma coisa.

Nomenclatura e how2use:

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

definição:

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

onde é usado:

É usado em expressões lambda e métodos anônimos.

0
overRideKode