“Func<Task<T>>” 的正确使用方式。

huangapple go评论64阅读模式
英文:

Correct way of using Func<Task<T>>

问题

第一个代码段:

static void Main(string[] args)
{
    Func<Task<int>> getter = async () => await Get();

    int x = getter().Result;

    Console.WriteLine("hello : " + x);
}

static async Task<int> Get()
{
     await Task.Delay(1000);
     return 1;
}

第二个代码段:

static void Main(string[] args)
{
     Func<Task<int>> getter = () => Get();

     int x = getter().Result;

     Console.WriteLine("hello : " + x);
}

static async Task<int> Get()
{
      await Task.Delay(1000);
      return 1;
}

请注意,这两个代码段的主要区别在于第一个代码段中,getter 是一个异步 lambda 表达式,其中使用了 await 来等待 Get() 方法的完成。而在第二个代码段中,getter 直接指向了 Get() 方法,没有使用异步关键字。

英文:

I have two code section in both of them I'm trying to use Func<Task<T>>. Can anyone point out the difference between below two code sections. Which approach should I follow.

static void Main(string[] args)
{
    Func&lt;Task&lt;int&gt;&gt; getter = async () =&gt; await Get();

    int x = getter().Result;

    Console.WriteLine(&quot;hello : &quot; + x);
}
        
static async Task&lt;int&gt; Get()
{
     await Task.Delay(1000);
     return 1;
}

static void Main(string[] args)
{
     Func&lt;Task&lt;int&gt;&gt; getter = () =&gt; Get();

     int x = getter().Result;

     Console.WriteLine(&quot;hello : &quot; + x);
}
        
static async Task&lt;int&gt; Get()
{
      await Task.Delay(1000);
      return 1;
}

答案1

得分: 2

这是您提供的代码的翻译:

不同之处在于错误时的堆栈跟踪。如果您稍微修改代码如下,您会看到结果的差异。

    内部类 Example1
    {
        public static void Main(string[] args)
        {
            Func<Task<int>> getter = async () => await Get();

            int x = getter().Result;

            Console.WriteLine("hello : " + x);
        }

        static async Task<int> Get()
        {
            throw new Exception("test");
            await Task.Delay(1000);
            return 1;
        }
    }

    内部类 Example2
    {
        public static void Main(string[] args)
        {
            Func<Task<int>> getter = () => Get();

            int x = getter().Result;

            Console.WriteLine("hello : " + x);
        }

        static async Task<int> Get()
        {
            throw new Exception("test");
            await Task.Delay(1000);
            return 1;
        }
    }

当 Example1 抛出异常时,你会看到以下堆栈跟踪。通过查看堆栈跟踪,你知道 Get() 方法是从匿名方法中调用的。

    此异常最初是在此调用堆栈上引发的:
        StackOverflow.CSharp.Example1.Get() 在 Example.cs 中
        StackOverflow.CSharp.Example1.Main.AnonymousMethod__0_0() 在 Example.cs 中

然而,当 Example2 抛出异常时,堆栈跟踪被减少,不显示 Get() 被调用的位置。追踪潜在问题可能会很困难。

    此异常最初是在此调用堆栈上引发的:
        StackOverflow.CSharp.Example2.Get() 在 Example.cs 中

在我们的项目中,我们更喜欢使用带有 async/await 的第一种方法。
英文:

The difference is a stacktrace in case of errors. If you modify the code a bit as following, you would see how results differ.

internal class Example1
{
    public static void Main(string[] args)
    {
        Func&lt;Task&lt;int&gt;&gt; getter = async () =&gt; await Get();

        int x = getter().Result;

        Console.WriteLine(&quot;hello : &quot; + x);
    }

    static async Task&lt;int&gt; Get()
    {
        throw new Exception(&quot;test&quot;);
        await Task.Delay(1000);
        return 1;
    }
}

internal class Example2
{
    public static void Main(string[] args)
    {
        Func&lt;Task&lt;int&gt;&gt; getter = () =&gt; Get();

        int x = getter().Result;

        Console.WriteLine(&quot;hello : &quot; + x);
    }

    static async Task&lt;int&gt; Get()
    {
        throw new Exception(&quot;test&quot;);
        await Task.Delay(1000);
        return 1;
    }
}

When Example1 throws the exception, you see a stacktrace as following. Looking at the stacktrace, you know that the Get() method was called from the anonymous method.

This exception was originally thrown at this call stack:
    StackOverflow.CSharp.Example1.Get() in Example.cs
    StackOverflow.CSharp.Example1.Main.AnonymousMethod__0_0() in Example.cs

However, when Example2 throws the exception, the stacktrace is reduced and does not show where Get() was called from. It could be hard to trace a potential problem.

This exception was originally thrown at this call stack:
    StackOverflow.CSharp.Example2.Get() in Example.cs

In our projects, we prefer the first approach with async/await.

答案2

得分: 0

以下是翻译好的内容:

对于像这样的简单情况,两个示例基本上会做相同的事情。

作为一个经验法则,您应该在可以的地方直接返回任务,而不是等待。也就是说,在您调用返回任务的单个方法而不对结果进行任何处理的情况下。但这主要是出于代码风格的原因,即避免可能会使读者困惑的不必要关键字。因此,示例2将被优先选择。

当然,在您特定的示例中,您可能根本不应该使用异步,而是更倾向于使用 Thread.Sleep。在UI程序中,您应避免使用 .Result,因为它会阻塞UI线程,并增加了死锁的风险。

英文:

For a trivial case like this both examples will do more or less the same thing.

As a rule of thumb you should return the task directly without awaiting where you can. I.e. in cases where you call a single method that returns a task and do not do any processing of the result. But this is mostly for code style reasons, i.e. avoiding unnecessary keywords that might confuse a reader. So example 2 would be preferred.

Ofc. In your specific example you should probably not use async at all, and prefer Thread.Sleep. In a UI program you should avoid using .Result, since it will block the UI thread, and increases the risk of deadlocks.

huangapple
  • 本文由 发表于 2023年4月11日 14:06:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/75982824.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定