什么时候应该使用 C# Generic Func<T,Tresult>?
When should I use C# Generic Func<T,Tresult>?
我一直在研究 C# 的泛型函数委托 (Func) 功能。
示例:
// Instantiate delegate to reference UppercaseString method
Func<string, string> convertMethod = UppercaseString;
string name = "Dakota";
// Use delegate instance to call UppercaseString method
Console.WriteLine(convertMethod(name));
我正在努力想一个现实生活场景,在这个场景中它们可能对我自己的应用程序有用。所以我想我会把问题放在那里。 \
非常感谢您的想法。
假设需要为报告目的计时方法调用。出于维护原因,将计时代码放在函数内是不可行的。
所以使用Func
调用可以进行时间操作而不干扰:
static void Time<T>(Func<T> work)
{
var sw = Stopwatch.StartNew();
var result = work();
Console.WriteLine(sw.Elapsed + ": " + result);
}
然后用我们要计时的函数调用它
Time(() => { return "Jabberwocky"; });
结果:
00:00:00.0000926: Jabberwocky
这里是 Funct<T,TResult>
使用相同的时间主题来计时正则表达式与 string.split
的示例
var strData = "The rain in Spain";
Time((str) => { return Regex.Split(str, @"\s"); }, strData);
Time((str) => { return str.Split(); }, strData);
这是设置
static void Time<T,S>(Func<T,S> work, T strToSplit)
{
var sw = Stopwatch.StartNew();
var result = work(strToSplit);
sw.Stop();
var joined = string.Join(", ", result as string[]);
Console.WriteLine("{0} : {1}", sw.Elapsed, joined);
}
结果
00:00:00.0000232 : The, rain, in, Spain
00:00:00.0000021 : The, rain, in, Spain
更新 2017 年的答案。这不是 Func
,而是它的非 return 兄弟 Action
;我发现我在我的 classes 上使用了一种基本形式的日志记录,我使用来自消费者的 Dependency Injection,例如:
Action<string> ReportIt { get; set; }
public void ReportStatus(Action<string> statusReporter)
{
ReportIt = statusReporter;
}
想法是状态报告是可选的,所以稍后在代码中我检查它是否可行,如果是我给出状态:
ReportIt?.Invoke("Running - Connection initiated");
class 的消费者这样称呼它
piperInstance.ReportStatus( Console.WriteLine );
也可以表示为
piperInstance.ReportStatus((str) => { Console.WriteLine(str); } );
评论有点长(制作社区维基)
解释用例的最佳方式是针对功能 map
功能。
在 C# 中称为 Select
。
假设您有一个字符串列表,map
将允许您更改字符串。
如你的例子:
somewords.Select(Uppercase)
或
somewords.Select(x = Uppercase(x))
或
somewords.Select(x => x.ToUpper())
但不限于字符串到字符串的转换。假设你想得到所有字符串长度的列表,你可以简单地做:
somewords.Select(x => x.Length)
上面将 return 整数列表(或您的语言中使用的任何内容)。
一旦开始将 this 与其他高阶函数结合起来,事情就会变得有趣:)
回到前面的例子,假设你想要最长字符串的长度。您有多种选择:
somewords.Select(x => x.Length).Max()
somewords.OrderByDescending(x => x.Length).First()
// or even
somewords.Max(x => x.Length)
可能还有一些我错过了。最后,这一切都可以帮助您表达您的意图,而无需做很多额外的工作(如上一个示例)。
注:
在上面的每种情况下,x => x.Length
都可以替换为任何与获取长度相同的函数,比如 Foo
。例如
int Foo(string s)
{
return s.Length;
}
所以somewords.Max(Foo)
是一样的
刚才看到一个有趣的真实例子:
public static List<Transaction> ReadAll(Settings settings, Func<string, FileInfo[]> getFilesOfType)
{
FileInfo[] files = getFilesOfType("CashRevenue");
// does stuff with files
}
由
调用
var transactions = FileParser.ReadAll(m_status.FilesByType)
它提供函数 FilesByType 作为参数
public FileInfo[] FilesByType (string type)
{
return result = Files.Where(f => f.Type == type)
.Select(f => new FileInfo(f.LocalPath))
.ToArray();
}
其中 Files 是一个集合 属性,可以使用 FilesByType
的字符串参数进行过滤
我一直在研究 C# 的泛型函数委托 (Func) 功能。
示例:
// Instantiate delegate to reference UppercaseString method
Func<string, string> convertMethod = UppercaseString;
string name = "Dakota";
// Use delegate instance to call UppercaseString method
Console.WriteLine(convertMethod(name));
我正在努力想一个现实生活场景,在这个场景中它们可能对我自己的应用程序有用。所以我想我会把问题放在那里。 \
非常感谢您的想法。
假设需要为报告目的计时方法调用。出于维护原因,将计时代码放在函数内是不可行的。
所以使用Func
调用可以进行时间操作而不干扰:
static void Time<T>(Func<T> work)
{
var sw = Stopwatch.StartNew();
var result = work();
Console.WriteLine(sw.Elapsed + ": " + result);
}
然后用我们要计时的函数调用它
Time(() => { return "Jabberwocky"; });
结果:
00:00:00.0000926: Jabberwocky
这里是 Funct<T,TResult>
使用相同的时间主题来计时正则表达式与 string.split
var strData = "The rain in Spain";
Time((str) => { return Regex.Split(str, @"\s"); }, strData);
Time((str) => { return str.Split(); }, strData);
这是设置
static void Time<T,S>(Func<T,S> work, T strToSplit)
{
var sw = Stopwatch.StartNew();
var result = work(strToSplit);
sw.Stop();
var joined = string.Join(", ", result as string[]);
Console.WriteLine("{0} : {1}", sw.Elapsed, joined);
}
结果
00:00:00.0000232 : The, rain, in, Spain
00:00:00.0000021 : The, rain, in, Spain
更新 2017 年的答案。这不是 Func
,而是它的非 return 兄弟 Action
;我发现我在我的 classes 上使用了一种基本形式的日志记录,我使用来自消费者的 Dependency Injection,例如:
Action<string> ReportIt { get; set; }
public void ReportStatus(Action<string> statusReporter)
{
ReportIt = statusReporter;
}
想法是状态报告是可选的,所以稍后在代码中我检查它是否可行,如果是我给出状态:
ReportIt?.Invoke("Running - Connection initiated");
class 的消费者这样称呼它
piperInstance.ReportStatus( Console.WriteLine );
也可以表示为
piperInstance.ReportStatus((str) => { Console.WriteLine(str); } );
评论有点长(制作社区维基)
解释用例的最佳方式是针对功能 map
功能。
在 C# 中称为 Select
。
假设您有一个字符串列表,map
将允许您更改字符串。
如你的例子:
somewords.Select(Uppercase)
或
somewords.Select(x = Uppercase(x))
或
somewords.Select(x => x.ToUpper())
但不限于字符串到字符串的转换。假设你想得到所有字符串长度的列表,你可以简单地做:
somewords.Select(x => x.Length)
上面将 return 整数列表(或您的语言中使用的任何内容)。
一旦开始将 this 与其他高阶函数结合起来,事情就会变得有趣:)
回到前面的例子,假设你想要最长字符串的长度。您有多种选择:
somewords.Select(x => x.Length).Max()
somewords.OrderByDescending(x => x.Length).First()
// or even
somewords.Max(x => x.Length)
可能还有一些我错过了。最后,这一切都可以帮助您表达您的意图,而无需做很多额外的工作(如上一个示例)。
注:
在上面的每种情况下,x => x.Length
都可以替换为任何与获取长度相同的函数,比如 Foo
。例如
int Foo(string s)
{
return s.Length;
}
所以somewords.Max(Foo)
是一样的
刚才看到一个有趣的真实例子:
public static List<Transaction> ReadAll(Settings settings, Func<string, FileInfo[]> getFilesOfType)
{
FileInfo[] files = getFilesOfType("CashRevenue");
// does stuff with files
}
由
调用var transactions = FileParser.ReadAll(m_status.FilesByType)
它提供函数 FilesByType 作为参数
public FileInfo[] FilesByType (string type)
{
return result = Files.Where(f => f.Type == type)
.Select(f => new FileInfo(f.LocalPath))
.ToArray();
}
其中 Files 是一个集合 属性,可以使用 FilesByType
的字符串参数进行过滤