使用通用函数的异步方法<T>

Async Method Using Generic Func<T>

我写了下面的方法:

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. 我可以将任何 无参数 method/function 传递给此方法以获得任何类型的 return 类型。例如:

     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<T>方法没有引用参数,我觉得 这应该是可能的。

Is there a way I could make this method more generic so that I could pass methods with parameters as well?

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 传递任意数量的参数,但它们必须是同一类型,除非您以某种形式使用运行时多态性。

Is there a way I could make this method more generic

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

    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() 方法 return 设为一项任务。假设在您的情况下调整起来很容易。)

  1. 你可以稍微缩短为
Task<T> Load<T>(Func<T> function)
{
    return Task.Factory.StartNew(() =>
    {
        IsLoading = true;
        var functionResult = function.Invoke();
        IsLoading = false;

        return functionResult;
    });
}

async 和 await 是可选的,因为您可以 return 任务。 在 finally

中使用 try ... finally 块并更新 IsLoading = false 可能是有意义的
  1. 我同意 Robert Harvey 关于附加参数的观点

Can I simplify the code?

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

  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;
  }
}

Is there a way I could make this method more generic so that I could pass methods with parameters as well?

您可以为此使用 lambda:

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