Answers:
Func<T>
是用于方法的预定义委托类型,该方法返回该类型的某些值T
。
换句话说,您可以使用此类型来引用返回值的方法T
。例如
public static string GetMessage() { return "Hello world"; }
可以这样引用
Func<string> f = GetMessage;
Func<T>
是delegate TResult Func<out TResult>()
。没有参数。Func<T1, T2>
将是一个带有一个参数的函数。
static int OneArgFunc(this string i) { return 42; }
Func<int> f = "foo".OneArgFunc;
。=)
Extension
是仅由C#/ VB.Net编译器而不是CLR读取的属性。基本上,实例方法(与静态函数不同)具有隐藏的第0个“ this”参数。因此,1参数实例方法与2参数静态函数非常相似。然后,我们有代表存储目标对象和函数指针。代表可以将第一个参数存储在目标中,也可以不存储。
将其视为占位符。当您的代码遵循某种特定模式但不必绑定到任何特定功能时,此功能将非常有用。
例如,考虑Enumerable.Select
扩展方法。
此方法采用a 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);
在由代表担任占位符的情况下,我免除了不得不在类似情况下一遍又一遍地写出相同模式的麻烦。
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)));
等等
不过请记住,它是一个委托,有关更多高级信息,请参阅文档。
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));
}
}
}
需要使用您的组件的开发人员必须:
实施接口
将真实的课程传递给 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>
使您可以动态定义通用方法接口。
您可以定义输入是什么类型,以及输出是什么类型。简单明了。
也许添加一些信息还为时不晚。
和:
Func是在系统名称空间中定义的自定义委托,它允许您使用0到16个输入参数指向具有相同签名的方法(与委托一样)。
命名和使用方式:
Func<input_1, input_2, ..., input1_6, output> funcDelegate = someMethod;
定义:
public delegate TResult Func<in T, out TResult>(T arg);
使用位置:
它用于lambda表达式和匿名方法。