O que é Func<>
e para que é usado?
If you have a function that needs to return different types, depending on the parameters, you can use a Func delegate, specifying the return type.
O que é Func<>
e para que é usado?
If you have a function that needs to return different types, depending on the parameters, you can use a Func delegate, specifying the return type.
Respostas:
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 fazer referência a 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;
Func<T>
é delegate TResult Func<out TResult>()
. Sem argumentos. Func<T1, T2>
seria uma função que leva um argumento.
static int OneArgFunc(this string i) { return 42; }
Func<int> f = "foo".OneArgFunc;
. =)
Extension
atributo que só é lido pelos compiladores C # / VB.Net, não CLR. Basicamente, os métodos de instância (ao contrário das funções estáticas) têm um 0º parâmetro oculto "este". Portanto, o método de instância de 1 argumento é muito semelhante à função estática de 2 argumentos. Então, temos delegados que armazenam o objeto de destino e o ponteiro de função . Os delegados podem armazenar o primeiro argumento no destino ou não fazer isso.
Pense nisso como um espaço reservado. Pode ser muito útil quando você tem um código que segue um certo padrão, mas não precisa estar vinculado a nenhuma funcionalidade específica.
Por exemplo, considere o Enumerable.Select
método de extensão.
Este método assume uma Func<T, TResult>
função concreta em vez de qualquer. Isso permite que seja usado em qualquer contexto onde o padrão acima se aplique.
Por exemplo, digamos que tenho um List<Person>
e quero apenas o nome de todas as pessoas da 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);
De imediato, você pode ver como consegui alavancar o mesmo código que representa um padrão (com Select
) com dois funções diferentes ( p => p.Name
ep => p.Age
).
A alternativa seria escrever uma versão diferente de Select
cada vez que você quisesse varrer uma sequência para um tipo diferente de valor. Portanto, para obter 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 atuando como espaço reservado, eu me liberto de ter que escrever o mesmo padrão repetidamente em casos como este.
Func<T1, T2, ..., Tn, Tr>
representa uma função, que recebe argumentos (T1, T2, ..., Tn) e retorna Tr.
Por exemplo, se você tem 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 usaria o sqr:
f1(2);
Console.WriteLine(f2(f1(4)));
etc.
Porém, lembre-se de que é um delegado; para informações mais avançadas, consulte a documentação.
Acho Func<T>
muito útil quando crio um componente que precisa ser personalizado "na hora".
Veja este exemplo muito simples: um PrintListToConsole<T>
componente.
Um objeto muito simples que imprime essa lista de objetos no console. Você deseja permitir que o desenvolvedor que o utiliza personalize a saída.
Por exemplo, você deseja permitir que ele defina um tipo específico de formato de número e assim por diante.
Sem Func
Primeiro, você deve 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);
}
Em seguida, você deve 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:
implementar a interface
passe a aula 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();
}
}
Usar 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 Func<T, String>
tipo, 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 definir uma interface de método genérico em tempo real.
Você define qual é o tipo de entrada e qual é o tipo de saída. Simples e conciso.
Func<T1,R>
e os outros predefinidos genéricos Func
delegados ( Func<T1,T2,R>
, Func<T1,T2,T3,R>
e outros) são representantes genéricos que retornam o tipo do último parâmetro genérico.
Se você tem uma função que precisa retornar diferentes tipos, dependendo dos parâmetros, você pode usar um Func
delegado, especificando o tipo de retorno.
É apenas um delegado genérico predefinido. Usando-o, você não precisa declarar todos os delegados. Existe outro delegado predefinido Action<T, T2...>
, que é o mesmo, mas retorna vazio.
Talvez não seja tarde demais para acrescentar algumas informações.
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 0 a 16 parâmetros de entrada e que deve retornar algo.
Nomenclatura e como usar:
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.