我可以创建任何类型的扩展方法。 一旦这样的类型是例如int的Func。
我想为函数编写扩展方法,而不是函数的返回类型。
我可以用hacky的方式做到:
Func<int> getUserId = () => GetUserId("Email");
int userId = getUserId.Retry(2);
其中函数Retry是一个定义为的扩展方法:
public static T Retry<T>(this Func<T> func, int maxAttempts)
{
for (int i = 0; i < maxAttempts; i++)
{
try
{
return func();
}
catch
{
}
}
throw new Exception("Retries failed.");
}
我真正想做的是:
var userId = (() => GetUserId("Email")).Retry(2);
但编译器不会将该函数与T的Func协调。
我知道Roslyn中的静态,所以我可以做类似的事情:
Retry(() => GetUserId("Email"), 2);
但我发现这更难阅读。我真的希望我创建的辅助函数不受影响。
还有其他模式可以给我类似的结果,比如monadic表达式,或者使用链接(即将T转换为链类型,内部有一个T,然后我为Chain of T编写扩展方法) 。我对这种方法的问题是你必须通过强制转换为T的链来开始表达式,然后通过强制转换为T来结束表达式,这会引起读者的注意力远离我的业务逻辑。 / p>
我知道我可以在Chain of T to T上使用隐式投射,但这感觉它在幕后做了一些魔术。
那么是否有可能获得对函数的引用,而不是先执行它,几乎没有锅炉板代码?
我希望为任何类型的Func / Action编写以下内容:
var settings = LoadSettingsFromDatabase().Retry(2);
答案 0 :(得分:8)
Per this question,我认为答案是“不”。
我建议你使用Retry
的静态包含,如你所知:
Retry(() => GetUserId("Email"), 2);
它使意图明确,它简单,可读性足够,而且它是惯用的C#。
我不喜欢的想法:
如果你愿意改变你的方法论点,那么下面的方法就可以了(但我认为大多数人会认为它非常糟糕):
public static T AttemptsAt<T>(this int maxAttempts, Func<T> func)
{
for (int i = 0; i < maxAttempts; i++)
{
try
{
return func();
}
catch
{
}
}
throw new Exception("Retries failed.");
}
用法:
var userId = 2.AttemptsAt(() => GetUserId("Email"));
答案 1 :(得分:1)
当然,如果你需要一个单行,你必须明确地转换为所需的委托类型:
var userId = ((Func<int>)(() => GetUserId("Email"))).Retry(2);
答案 2 :(得分:0)
我不知道它是否回答了您的问题,但似乎可以定义Func(或Action)的扩展方法,请参阅:
http://www.codeproject.com/Articles/1104555/The-Function-Decorator-Pattern-Reanimation-of-Func。引用乔丹先生:
public static Func<TArg, TResult> RetryIfFailed<TArg, TResult>
(this Func<TArg, TResult> func, int maxRetry) {
return (arg) => {
int t = 0;
do {
try {
return func(arg);
}
catch (Exception) {
if (++t > maxRetry) {
throw;
}
}
} while (true);
};
}
....
// get the method we are going to retry with
Func<DateTime, string> getMyDate = client.GetMyDate;
// intercept it with RetryIfFailed interceptor, which retries once at most
getMyDate = getMyDate.RetryIfFailed(1);
for (var i = 0; i < TimesToInvoke; i++) {
try {
// call the intercepted method instead of client.GetMyDate
getMyDate(DateTime.Today.AddDays(i % 30));
counter.TotalSuccess++;
}
catch (Exception ex) {counter.TotalError++; }
....
答案 3 :(得分:0)
问题在于成员方法不能将自身隐式转换为Func-在我看来,这很奇怪,但也许有一个很好的解释:)。
无论如何,这就是我所说的Func扩展名:
var userId = ((Func<int>)GetUserId("Email")).Retry(2);