【问题标题】:How to pass code blocks (not full methods) as arguments in C#?如何在 C# 中将代码块(不是完整方法)作为参数传递?
【发布时间】:2013-10-17 16:59:10
【问题描述】:

我正在 csharp (.net 4.0) 中构建一个消息传递应用程序,我的班级有发送/接收消息的基本方法:

void sendMessage( string msgBody, string properties);
object getNextMessage();
object getMessageById( string msgId);

这些方法中的每一个都依赖于底层连接;如果连接过时,我会使用 try/catch 和一些重试逻辑进行额外尝试,如下所示:

public object getNextMessage(){
   object nextMessage = null;
   int retryAttempts = 0;
   int MAX_ATTEMPTS = 3;

   while( retryAttempts < MAX_ATTEMPTS){
      retryAttempts++;
      try{
         nextMessage = connection.getMessage("queueName");
      }catch(Exception e){   
      }
   }
   return nextMessage;
}

由于重试逻辑是通用的,我想避免在每个方法中重复相同的代码。我想创建一个通用的重试函数并执行以下操作:

public object makeAttempt( CodeBlock codeBlock){
       while( retryAttempts < MAX_ATTEMPTS){
          retryAttempts++;
          try{
             return codeBlock.invoke()
          }catch(Exception e){   
          }
       }
       return null;
}

..我想像这样使用makeAttempt,或者类似的东西:

public object getNextMessage(){       
   makeAttempt() => {
      return connection.getMessage("queueName");
   }
}

我查看了this,但它涉及将整个函数作为参数传递,我没有这样做。我还查看了.net Lambda Expressions,但我没有看到连接。

我没有做太多 C#,所以请原谅 n00b 的问题 :-)

【问题讨论】:

  • 您已经准确查找了解决此问题所需的工具。 lambda 只不过是一种用于创建新方法的简洁语法,这正是您想要的。
  • 代码块或匿名函数 (lambda) 正是您所需要的。这就是你传递代码块的方式
  • @Servy:字面意思尚未完成 - makeAttemptgetNextMessage 中的代码还不能编译。
  • @JonSkeet 对,我的意思是他已经有了问题的答案。措辞不好。
  • 旁注;与其忽略重试导致的所有异常,不如考虑保留它们并在所有引退失败的情况下将它们重新抛出AggregateException,而不是返回null。既是因为null 可能是函数调用的有效结果,又因为调用者有关于失败原因的有意义的信息。

标签: c# .net c#-4.0 delegates


【解决方案1】:

您快到最后了 - 您只需将 lambda 表达式包含在 () 中,因为它是一个方法参数。您还需要使用来自makeAttempt 的返回值来为您的getNextMessage 方法提供返回值。所以:

public object getNextMessage(){       
   return makeAttempt(() => {
      return connection.getMessage("queueName");
   });
}

或者更简单地说,使用表达式 lambda:

public object getNextMessage(){       
   return makeAttempt(() => connection.getMessage("queueName"));
}

这都是假设CodeBlock是一个委托类型,当然,例如

public delegate object CodeBlock();

您还需要将makeAttempt 更改为调用Invoke 而不是invoke - C# 区分大小写。我强烈建议您也遵循 .NET 命名约定,其中方法是 PascalCased 而不是 camelCased

编辑:如 cmets 中所述,您可以将其设为通用:

public T CallWithRetries<T>(Func<T> function)
{
    for (int attempt = 1; attempt <= MaxAttempts; attempt++)
    {
        try
        {
            return function();
        }
        catch(Exception e)
        {
            // TODO: Logging
        }
    }
    // TODO: Consider throwing AggregateException here
    return default(T);
}

【讨论】:

  • @Servy:嗯,它与真实代码非常接近,值得将其改正为 be 真实代码。
  • 啊,非常好,所以我非常接近!我可以将相同的makeAttempt() 用于不返回任何内容的代码块吗?还是我必须创建两个版本的makeAttempt()
  • @raffian 最好的选择是制作两个版本;其他任何事情都是 hack,通常涉及返回一个您不关心的值。
  • @raffian 是的,您可以将Action 用于程序代码块,将Func&lt;T&gt; 用于返回结果的功能代码块。
  • @raffian:是的,您可以使用 Func&lt;object&gt; 代替 - 或使其通用。我会将其编辑到我的答案中。
猜你喜欢
  • 2013-09-04
  • 2018-10-21
  • 1970-01-01
  • 2011-01-28
  • 2018-09-09
  • 1970-01-01
  • 2020-08-12
  • 1970-01-01
相关资源
最近更新 更多