函数的扩展方法

时间:2016-03-02 23:42:15

标签: c#

我可以创建任何类型的扩展方法。 一旦这样的类型是例如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);

4 个答案:

答案 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);