Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
873 views
in Technique[技术] by (71.8m points)

c# - Local functions benefits with async methods

According to the Microsoft Docs:

There are two common use cases for local functions: public iterator methods and public async methods. Both types of methods generate code that reports errors later than programmers might expect. ... The technique can be employed with async methods to ensure that exceptions arising from argument validation are thrown before the asynchronous work begins:

public Task<string> PerformLongRunningWork(string address, int index, string name)
{
    if (string.IsNullOrWhiteSpace(address))
        throw new ArgumentException(message: "An address is required", paramName: nameof(address));
    if (index < 0)
        throw new ArgumentOutOfRangeException(paramName: nameof(index), message: "The index must be non-negative");
    if (string.IsNullOrWhiteSpace(name))
        throw new ArgumentException(message: "You must supply a name", paramName: nameof(name));

    return longRunningWorkImplementation();

    async Task<string> longRunningWorkImplementation()
    {
        var interimResult = await FirstWork(address);
        var secondResult = await SecondStep(index, name);
        return $"The results are {interimResult} and {secondResult}. Enjoy.";
    }
}

I don't really understand this example. If we just get rid of the local function and extract its contents to the outer scope and put them right after the validation, won't we achieve the same result? What does the local function actually give in this case?

If it actually doesn't improve anything, can you come up with a better example?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

There's a difference between:

  1. Call PerformLongRunningWork and it throws an exception.
  2. Call PerformLongRunningWork and it successfully executes, and gives you back a Task<string> which contains an exception.

That is:

Task<string> task;
try
{
    task = PerformLongRunningOperation();
}
catch (Exception e)
{
    // PerformLongRunningOperation itself threw
}

bool containsException = task.IsFaulted;

try
{
    string result = await task;
}
catch (Exception e)
{
    // The Task<string> returned from PerformLongRunningWork contained an exception
}

If you throw an exception from an async Task method, that exception is wrapped up inside the Task which is returned.

Therefore, your example with a non-async method which delegates to an async local function will throw those ArgumentException directly when it's called, and not return them wrapped up in the Task<string> it returns.

If you rewrote the example to remove the local function and instead make PerformLongRunningWork async, then those ArgumentExceptions would be wrapped up inside the Task<string> returned.

Which one you want to do is a matter of debate.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...