C# 重构2个接收回调函数的相同函数<;任务<;T>&燃气轮机;和Func<;T>;分别地 公共静态异步任务NDC(Func Func) { 使用(SomeDisposable()) { 尝试 { return wait func(); } 捕获(例外情况除外) { 控制台写入线(“错误”); 投掷; } } } 公共静态T NDC(Func Func) { 使用(SomeDisposable()) { 尝试 { 返回func(); } 捕获(例外情况除外) { 控制台写入线(“错误”); 投掷; } } }

C# 重构2个接收回调函数的相同函数<;任务<;T>&燃气轮机;和Func<;T>;分别地 公共静态异步任务NDC(Func Func) { 使用(SomeDisposable()) { 尝试 { return wait func(); } 捕获(例外情况除外) { 控制台写入线(“错误”); 投掷; } } } 公共静态T NDC(Func Func) { 使用(SomeDisposable()) { 尝试 { 返回func(); } 捕获(例外情况除外) { 控制台写入线(“错误”); 投掷; } } },c#,multithreading,generics,async-await,task,C#,Multithreading,Generics,Async Await,Task,如您所见,这两个函数几乎相同。它们之间唯一的区别是,当回调是Func时,它将被等待,因此返回类型是Task,否则,函数将返回类型T 我想对这些函数进行分组/泛型,有什么方法可以实现吗?提前谢谢 注意:如果调用方调用了非任务版本,则此函数中的所有进程都应在单个线程中运行。您可以从异步版本派生同步版本,如下所示: public static async Task<T> NDC<T>(Func<Task<T>> func) { using (So

如您所见,这两个函数几乎相同。它们之间唯一的区别是,当回调是
Func
时,它将被等待,因此返回类型是
Task
,否则,函数将返回类型
T

我想对这些函数进行分组/泛型,有什么方法可以实现吗?提前谢谢


注意:如果调用方调用了非任务版本,则此函数中的所有进程都应在单个线程中运行。

您可以从异步版本派生同步版本,如下所示:

public static async Task<T> NDC<T>(Func<Task<T>> func)
{
    using (SomeDisposable())
    {
        try
        {
            return await func();
        }
        catch (Exception ex)
        {
            Console.WriteLine("error");

            throw;
        }
    }
}

public static T NDC<T>(Func<T> func)
{
    using (SomeDisposable())
    {
        try
        {
            return func();
        }
        catch (Exception ex)
        {
            Console.WriteLine("error");

            throw;
        }
    }
}
publicstatictdc(Func-Func)
{
如果(func==null)抛出新的ArgumentNullException(nameof(func));
返回NDC(()=>Task.FromResult(func()).GetAwaiter().GetResult();
}
有两个陷阱:

  • 您不能只使用一种方法进行参数验证。必须在两个
    NDC
    方法中验证
    func
    参数。除非使用可为空的引用类型,如注释中的Alexei Levenkov

  • 此解决方案仅在
    等待func()时有效
    是异步
    NDC
    方法中唯一等待的操作。否则,当前线程将被阻塞,坏事情将发生(可伸缩性降低或死锁,取决于应用程序的类型)


  • 您可以从异步版本派生同步版本,如下所示:

    public static async Task<T> NDC<T>(Func<Task<T>> func)
    {
        using (SomeDisposable())
        {
            try
            {
                return await func();
            }
            catch (Exception ex)
            {
                Console.WriteLine("error");
    
                throw;
            }
        }
    }
    
    public static T NDC<T>(Func<T> func)
    {
        using (SomeDisposable())
        {
            try
            {
                return func();
            }
            catch (Exception ex)
            {
                Console.WriteLine("error");
    
                throw;
            }
        }
    }
    
    publicstatictdc(Func-Func)
    {
    如果(func==null)抛出新的ArgumentNullException(nameof(func));
    返回NDC(()=>Task.FromResult(func()).GetAwaiter().GetResult();
    }
    
    有两个陷阱:

  • 您不能只使用一种方法进行参数验证。必须在两个
    NDC
    方法中验证
    func
    参数。除非使用可为空的引用类型,如注释中的Alexei Levenkov

  • 此解决方案仅在
    等待func()时有效
    是异步
    NDC
    方法中唯一等待的操作。否则,当前线程将被阻塞,坏事情将发生(可伸缩性降低或死锁,取决于应用程序的类型)


  • 库作者提供方法的同步和异步变体是有原因的……它们被设计为以不同的方式调用,并具有不同的语义。这是一个糟糕的使用,最终会导致难以维护的代码。即使在using语句中包装disposables也可能不会使用相同的实现。让我删除它。这是库作者提供同步和异步方法变体的原因……它们被设计为以不同的方式调用,并具有不同的语义。这是一个不好用的干,最终会导致难以维护的代码。此外,考虑到引入<代码> IASYNCUnaby,即使在使用语句中包装处理也可能不使用相同的实现。我仍然会进入多线程环境,这是我想要的avoid@mannok我不知道并发是如何发生的,除非
    NDC
    方法中还有其他等待的操作,您还没有向我们展示。很好的一点是,您可以通过知道async one不会使用同步回调实际运行async来实际升级同步版本。。。两个注意事项:对于C#9,您有可为空的引用类型,它允许您跳过空检查。。。实际上,由于在这种情况下没有异步执行,您可以只执行
    .Result
    。。。(如果
    NDC
    除了有实际的异步调用之外,没有很好的理由尝试将它们结合起来)@AlexeiLevenkov我不太喜欢可空引用的概念。我看到这些卷曲的问号像语言污染一样随处可见。:-)关于使用
    .Result
    属性,这是否会在
    AggregateException
    中包装一个可能的异常?似乎如果我这样做,我仍然会进入我想要的多线程环境avoid@mannok我看不出并发是如何发生的,除非在
    NDC
    方法中还有其他等待的操作,您还没有向我们展示。很好的一点是,您可以通过知道async one不会实际使用同步回调运行async来升级同步版本。。。两个注意事项:对于C#9,您有可为空的引用类型,它允许您跳过空检查。。。实际上,由于在这种情况下没有异步执行,您可以只执行
    .Result
    。。。(如果
    NDC
    除了有实际的异步调用之外,没有很好的理由尝试将它们结合起来)@AlexeiLevenkov我不太喜欢可空引用的概念。我看到这些卷曲的问号像语言污染一样随处可见。:-)关于使用
    .Result
    属性,这是否会在
    聚合异常
    中包装一个可能的异常?