【问题标题】:Async Method Using Generic Func<T>使用通用 Func<T> 的异步方法
【发布时间】:2023-03-16 04:42:01
【问题描述】:

我写了如下方法:

async Task<T> Load<T>(Func<T> function)
{
    T result = await Task.Factory.StartNew(() =>
    {
        IsLoading = true;

        T functionResult = function.Invoke();

        IsLoading = false;

        return functionResult;
    });

    return result;
}

我有两个问题:

  1. 我可以简化代码吗?

  2. 我可以将任何 无参数 方法/函数传递给此方法以获得任何类型的返回类型。例如:

     string GetString()
    

    通过说:

     string someString = await Load(GetString);
    

    有没有一种方法可以使这个方法更通用,以便我可以传递带有参数的方法 好吧?例如。一种也可以接受的方法:

     string GetString(string someString)
     string GetString(string someString, int someInt)
    

    这可能看起来像:

     string someString = await Load(GetString("string"));
     string someString = await Load(GetString("string", 1));
    

    这显然行不通,但是由于Load&lt;T&gt;方法没有引用参数,我觉得 这应该是可能的。

【问题讨论】:

  • IsLoading的目的是什么?
  • 您通常不需要.Invoke() 代表。你可以简单地T functionResult = function()There is no difference 在生成的代码中,但我倾向于将 .Invoke() 与基于反射的动态调用相关联。
  • 你确定你甚至需要问题2吗? await Load(() =&gt; GetString("string")) 应该可以正常工作。
  • @RobertHarvey IsLoading 是我在 WPF 中用于绑定的属性 - 此方法是名为 LoadingViewModel 的基类的一部分。

标签: c# generics async-await task func


【解决方案1】:

我可以简化代码吗?

您既可以简化它,也可以使它更正确。编写的代码存在一些问题:

  1. IsLoading 是一个 UI 绑定属性,所以它应该在 UI 线程上更新,而不是后台线程。某些框架(如 Windows 上的 WPF)允许您改变规则,但其他基于 XAML 的框架则不允许。
  2. 如果加载失败,代码当前不会将IsLoading 设置为false
  3. Task.Factory.StartNew should be avoided; it's a dangerous, low-level method。如果您需要在后台线程上运行方法,请使用 Task.Run
async Task<T> Load<T>(Func<T> function)
{
  IsLoading = true;
  try
  {
    return await Task.Run(function);
  }
  finally
  {
    IsLoading = false;
  }
}

有没有办法让这个方法更通用,以便我也可以传递带参数的方法?

您可以为此使用 lambda:

string someString = await Load(() => GetString("string"));
string someString = await Load(() => GetString("string", 1));

【讨论】:

  • 感谢您的回答 - 很好的回答!
【解决方案2】:
  1. 您可以将其稍微缩短为
Task<T> Load<T>(Func<T> function)
{
    return Task.Factory.StartNew(() =>
    {
        IsLoading = true;
        var functionResult = function.Invoke();
        IsLoading = false;

        return functionResult;
    });
}

async 和 await 是可选的,因为您可以返回任务。 在 finally 中使用 try ... finally 阻止和更新 IsLoading = false 可能是有意义的

  1. 我同意 Robert Harvey answer 关于其他参数的看法

【讨论】:

    【解决方案3】:

    有没有办法让这个方法更通用,以便我也可以传递带参数的方法?

    async Task<R> Load<T, R>(Func<T, R> function, T parameter)
    {
        R result = await Task.Run(() =>
        {
            return function.Invoke(parameter);
        });
    
        return result;
    }
    

    您可以将函数需要的任何参数捆绑到T

    如果您仍需要单独的参数,则必须创建额外的重载。

    您也可以尝试params 传递任意数量的参数,但它们必须都是同一类型,除非您以某种形式使用运行时多态性。

    【讨论】:

      【解决方案4】:

      有没有办法让这个方法更通用

      一种选择是像这样嵌套它:

          public bool IsLoading { get; set; }
          
          public Task<string> GetString(string input) => Task.FromResult($"ret for input {input}");
      
          public async Task<TReturn> Load<TReturn>(Func<Task<TReturn>> cb)
          {
              IsLoading = true;
              var ret = await cb();
              IsLoading = false;
      
              return ret;
          }
      
          public async Task Demo()
          {
              var ret = await Load(() => GetString("input"));
          }
      

      (我也让 GetString() 方法返回了一个任务。假设如果这对你的情况没有意义,那么它很容易调整。)

      【讨论】:

        猜你喜欢
        • 2016-11-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-08-16
        相关资源
        最近更新 更多