【问题标题】:Use func method for an action also?也对动作使用 func 方法?
【发布时间】:2013-06-04 19:51:43
【问题描述】:

我创建了一个抽象 try/catch 功能的方法。我有大约 30 种方法具有完全相同的 try/catch 场景。所以我把它放在一种方法中:

    private T Invoke<T>(Func<T> func)
    {
        try
        {
            return func.Invoke();
        }
        catch (Exception ex)
        {
            throw LogAndThrowFaultException(ex);
        }
    }

现在,大多数方法都这样称呼它:

    public IEnumerable<PingResponse> GetAllForPingRequest(PingRequest pingRequest)
    {
        return Invoke(() => PingResponseLogic.GetAllForPingRequest(pingRequest));
    }

我的问题是我只有几个方法可以调用它而无需返回值:

Invoke<void>(() => CustomVariableGroupLogic.Delete(customVariableGroup));

但是,我不能在那里使用voidInvoke() 方法采用 func,但在这种情况下,它必须是 action。我做了一些研究,看起来我可能必须创建另一个 Invoke() 方法,但让它采取行动。不过,这些建议来自 2009 年和 2010 年。是否可以以某种方式使用我的 func 方法而不必创建另一个 Invoke() 方法,例如 Invoke2()

【问题讨论】:

    标签: c# action func


    【解决方案1】:

    一个快速而肮脏的解决方案是在调用 Delete 方法后添加 default 值。如果您不将 Invoke 方法的结果分配给变量,它将被忽略。例如,下面的代码演示了这一点:

    Invoke(() => {CustomVariableGroupLogic.Delete(customVariableGroup); return 0; });
    

    你可以看到类似的例子建议here

    如果您有很多这样的调用,您可以构建一个精美的包装器,它将为给定的Action 返回Func。示例:

    Func<Action, Func<int>> wrap = action => () => {action(); return 0;};
    

    现在可以

    Invoke(() => wrap(() => CustomVariableGroupLogic.Delete(customVariableGroup)));
    

    但这有点接近 lambda 的疯狂

    灵感来自 pcm2

    您可以为Invoke 创建一个重载,只需将Action 作为参数,使用上面提出的解决方案来调用Func&lt;T&gt; 的实现:

    public void Invoke(Action action)
    {
        Invoke(() => {action(); return 0;});
    }
    

    现在你可以

    Invoke(() => CustomVariableGroupLogic.Delete(customVariableGroup));
    

    【讨论】:

    • 哈。我一直在寻找这样的提示。谢谢!
    【解决方案2】:

    你能不能只创建一个调用的重载?

    public void Invoke<T>(Func<T> func){
        return (T) InvokeInternal(func);
    }
    
    public void Invoke(Action action){
        InvokeInternal(action);
    }
    

    如果您希望这两种方法都做完全相同的事情,并保持干燥,您可以创建一个像这样的方法:

    private object InvokeInternal(System.Delegate @delegate)
    {
        try
        {
            return @delegate.DynamicInvoke();
        }
        catch (Exception ex)
        {
            throw LogAndThrowFaultException(ex);
        }
    }
    

    让你的两个重载都调用这个方法,适当地转换结果,如果有的话

    【讨论】:

    • 请注意,采用Action 的重载可以只调用采用Func 的重载:Invoke&lt;int&gt;(() =&gt; { action(); return 0; });
    • 确实,但这看起来有点粗略,你不觉得吗?如果我遇到这样的代码,我将不得不停下来思考一秒钟
    • 我宁愿 far 拥有该代码而不是动态代码,因为动态代码会无缘无故地失去类型安全性。
    【解决方案3】:
    1. 你真的要func.Invoke()吗?为什么不只是func()
    2. 为什么你的Invoke 不是静态的?
    3. 根据LogAndThrowFaultException 正在做什么,这可能会非常糟糕。通过始终将内部异常设置为 ex,确保保留异常堆栈。

    但是,是的,您需要第二个返回 void 而不是 T 的函数。

    【讨论】:

      【解决方案4】:

      你可以像这样将动作包装成一个函数。或者您可以创建一个方便的函数来进行包装:

      void Main()
      {
          Action<string> a = s => Console.WriteLine(s);
      
          Invoke(() => { a(); return null; });
          Invoke(s);
      }
      
      private void Invoke(Action a)
      {
          Invoke(() => { a(); return null; };
      }
      

      【讨论】:

      • 要么指定类型参数,要么使用return 值,这将允许编译器推断类型参数 - 返回 null 是行不通的。
      • 你可以return 0;return (object)null;
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-16
      • 1970-01-01
      • 2018-06-21
      • 1970-01-01
      相关资源
      最近更新 更多