使用通用 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. 我可以将任何无参数方法/函数传递给此方法,以获取任何类型的返回类型。例如:

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

解决方法

我可以简化代码吗?

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

  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));
,

有没有一种方法,我可以让这个方法更加通用,这样我可以通过带参数的方法呢?

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

,
  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 关于附加参数的意见
,

有没有一种方法,我可以使这个方法更通用

一种选择是巢它像这样:

    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()方法返回一个任务了。假设它很容易调整如果让你的情况没有任何意义。)