【问题标题】:Is async partial void MyPartialMethod() dangerous?async partial void MyPartialMethod() 危险吗?
【发布时间】:2017-01-14 16:28:57
【问题描述】:

我看到很多关于编写代码的警告,例如...

public async void MyDangerousMethodWhichCouldCrashMyApp...

我已经读到它可以使用事件处理程序,因为它们必须返回 void。但是partial methods 也必须返回 void。可以有如下代码...

static void Main(string[] args)
{
    MainAsync().Wait();
    Console.ReadLine();
}

async static Task MainAsync()
{
    MyCodeGeneratedClass c = new MyCodeGeneratedClass();
    try
    {
        await c.MyCodeGeneratedMethod();
    }
    catch(Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
}

public partial class MyCodeGeneratedClass
{
    public async Task MyCodeGeneratedMethod()
    {
        HttpClient client = new HttpClient();
        Console.WriteLine(await client.GetStringAsync("http://msdn.microsoft.com"));
        MyCustomCode();
    }

    partial void MyCustomCode();
}

然后实现为...

partial class MyCodeGeneratedClass
{
    async partial void MyCustomCode()
    {
        HttpClient client = new HttpClient();
        Console.WriteLine(await client.GetStringAsync("http://msdn.microsoft.com"));
        throw new Exception("Boom");
    }
}

但是如果 MyCustomCode 的实现遇到异常,应用程序会发生什么?

如果不可行,考虑到 async / await 的流行程度,这是否意味着部分方法基本上已经过时了?代码生成系统是否应该停止公开部分方法以支持事件,或者最好还是在基类中清空受保护的虚拟方法?即

protected virtual Task MyCustomCode(T foo)
{
    return Task.FromResult(0);
}

编辑:好的,所以我对代码进行了几次更新。在我写的原始伪代码没有得到好评之后。我认为上面的代码表明 async partial void MyPartialMethod 肯定存在问题,因为对 MyCodeGeneratedMethod 的调用似乎确实降低了应用程序域,尽管调用周围的 try catch。我只是想知道是否有比迁移到受保护的虚拟基类方法更好的选择。

【问题讨论】:

  • 恐怕你的问题很不清楚。请考虑提供更多关于您的问题和您希望达到的目标的背景信息。
  • 好的...所以我已经为从未使用过代码生成、部分类或部分方法的读者详细说明了代码。
  • 我不使用部分方法,并且我完全理解了原始问题(尽管原始问题中的 async partial void MyPartialMethod(); 无法编译,因为 async 关键字只能出现在具有主体的方法上)。
  • 是的,很抱歉原来的代码是错误的......
  • 我会假设该方法在异常方面的行为与任何其他异步 void 方法完全相同。至于部分方法是否过时,我无法评论,因为我不使用它们,但如果你用虚拟方法替换它们,这些方法应该是 virtual Task (即不是异步)并返回 Task.CompletedTask (如果它们必须是虚拟的,不能是抽象的)。但这只是我的一个小问题。

标签: c# asynchronous async-await


【解决方案1】:

但是如果 MyCustomCode 的实现遇到异常,应用会发生什么?

async void 方法的语义是在方法开始时的当前 SynchronizationContext 上直接引发异常。我的async best practices 文章中介绍了有关async void 的这个和其他有趣的事实。

这是否意味着部分方法基本上已经过时了?

与事件处理程序一样过时。所以,不,不是。但是,它们并未更新为允许返回类型为 Task,因此它们似乎是一种语言功能,并未与其他语言一起积极更新。

代码生成系统是否应该停止公开部分方法以支持事件,或者最好还是在基类中清空受保护的虚拟方法?

事件根本不会改变这一点;它们仍将使用async void 实现。

如果“钩子”需要异步,那么这会更改所有生成的代码,因为它也必须都是异步的。

只有在生成的代码需要来自该部分方法的某些结果时,异步部分方法才会起作用并且实现必须执行一些异步工作才能生成该结果。根据我的经验,部分方法在概念上更像事件,所以async void 是可以接受的。

【讨论】:

  • 在事件和部分方法的情况下,您认为将异步代码包装在 try/catch 中是否明智/合适,正如我在下面发布的答案中所描述的那样?
  • @Mick:是的,我愿意。
【解决方案2】:

对于使用 Xamarin 的人来说,这似乎是一个大问题,Xamarin 生成的代码中包含许多作为部分方法实现的钩子,正如在这个线程中看到的那样......

https://forums.xamarin.com/discussion/2766/cannot-await-in-an-async-partial-method

正如我在那里发布的那样,我会向任何正在考虑在部分方法中编写异步代码以将其异步代码包装在 try catch 中的人推荐如下...

public partial class MyCodeGeneratedClass
{
    public async Task MyCodeGeneratedMethod()
    {
        HttpClient client = new HttpClient();
        Console.WriteLine(await client.GetStringAsync("http://msdn.microsoft.com"));
        MyCustomCode();
    }

    partial void MyCustomCode();
}

partial class MyCodeGeneratedClass
{
    async partial void MyCustomCode()
    {
        try
        {
            await MyCustomCodeAsync();
        }
        catch(Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }

    protected async Task MyCustomCodeAsync()
    {
        HttpClient client = new HttpClient();
        Console.WriteLine(await client.GetStringAsync("http://msdn.microsoft.com"));

        throw new Exception("Boom");
    }
}

尝试和捕捉异步任务方法应该可以保护应用程序域。此外,如果/当 Microsoft improve partial methods 时,它也可能使重构代码更容易。

由于我可以控制在我的项目中生成代码的脚本,因此我选择在可能存在 I/O 且需要异步的情况下不公开部分方法,而是提供空的受保护虚拟任务基类中的方法。此外,生成的代码需要能够等待 CustomCode 方法,似乎没有办法使用异步部分方法来做到这一点。

【讨论】:

    猜你喜欢
    • 2011-12-31
    • 2018-10-17
    • 2015-03-08
    • 1970-01-01
    • 1970-01-01
    • 2013-06-16
    • 2017-05-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多