【问题标题】:Can I refactor code to fix "Asynchronous method should not return void"?我可以重构代码以修复“异步方法不应返回 void”吗?
【发布时间】:2016-11-23 11:21:40
【问题描述】:

我正在使用 Xamarin 在 C# 中做一个项目。编译器警告我“异步方法 'HandleWidget_ClickButton' 不应返回 void”。请参阅此处的示例:

//Code is simplified

public class Widget {
   public event Action<int> ClickButton;

   private void FireClickButton (int id)
   {
       if (ClickButton != null) {
           ClickButton (id);
       }
   }

   //somewhere else i call FireClickButton(1);
}

public class MyFragment {
    private _widget Widget;

    public override View OnCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        //...
        _widget = view.FindViewById<Widget> (Resource.Id.widget);
        //...
    }


    public override void OnResume ()
    {
        base.OnResume ();
        _widget.ClickButton += HandleWidget_ClickButton;
    }

    async void HandleWidget_ClickButton (int id)
    {
        await SaveSomethingInStorage (id);
    }
}

我能否以某种方式将某些内容返回给事件/动作/委托?我不知道这是否可能,或者如何在语法方面编写它,并且我花了相当长的时间寻找解决方案。在其他地方我读到,在处理事件时,可以使用 async void(而不是 async Task 之类的东西),但我喜欢避免警告,也不喜欢使用 #Pragma 指令隐藏它们。

编辑(有答案)来自@hvd:

您创建了一个事件处理程序,但您没有遵循 .NET 事件处理程序的约定。你应该。警告是 自动抑制可检测为事件处理程序的内容。

事件处理程序的 .NET 约定需要发送者,类型为 对象和事件参数,类型为 EventArgs 或派生类 来自 EventArgs。如果您使用此签名,则不应再获得 警告。

我这样重写了我的代码:

public class MyEventArgs : EventArgs
{
    public MyEventArgs (int id)
    {
        ID = id;
    }

    public int ID;
}

public class Widget {
    public event EventHandler<MyEventArgs> ClickTest;

    void FireClickButton (int id)
    {
        if (ClickTest != null) {
            ClickTest (this, new MyEventArgs (id));
        }
    }
}

//In observer class
_widget.ClickTest += HandleWidget_ClickTest;

async void HandleWidget_ClickTest (object sender, MyEventArgs e)
{
    await DoSomethingAsync (e.ID);
}

请注意,您必须从 EventArgs 派生。这样做不会抑制警告:

public event EventHandler<int> AnotherClickTest;

if (AnotherClickTest != null) {
   AnotherClickTest (this, 1);
}

【问题讨论】:

标签: c# asynchronous


【解决方案1】:

您创建了一个事件处理程序,但您没有遵循事件处理程序的 .NET 约定。你应该。对于可以检测为事件处理程序的内容,该警告会被自动抑制。

事件处理程序的 .NET 约定需要 object 类型的发送方和 EventArgs 类型的事件参数或派生自 EventArgs 的类。如果您使用此签名,则不应再收到警告。

【讨论】:

  • 实际上,在 VS2015 中我根本没有收到任何警告。
  • @smoksnes 我愿意。 VS 有多个扩展提供额外的警告,它可能来自其中之一。在我的情况下,我会检查它的来源。
  • 你可能是对的。我正在使用 Resharper 2016 和 Visual Studio 2015。但重点仍然存在 - 您可以在异步事件处理程序中使用 void。
  • @smoksnes 就我而言,警告来自SonarLint。我不确定这是否也是 OP 正在使用的,但已经确定 async void 对于事件处理程序是可以的,所以我希望如果 OP 使用其他东西,其他东西使用相同的逻辑为事件处理程序创建一个例外。
  • @GerhardSchreurs int 不是从 EventArgs 派生的。您应该创建一个派生自EventArgs 的类(如果您愿意,可以将其称为WidgetClickEventArgs),并将int 存储在该类的属性中。或者,如果id 仅表示点击了哪个按钮,只需有EventArgs e 并传递EventArgs.Empty,那么您可以检查sender 而不是id
【解决方案2】:

异步方法总是应该返回一个任务。 Task 表示尚未完成的操作。之后,您可以对任务执行 Wait() 以获得完成的结果......因为您的方法是 Void,所以您不会做任何等待。签名应如下所示:

async Task HandleWidget_ClickButton (int id)

如果你想返回一些东西,它会是这样的(例如,int):

async Task<int> HandleWidget_ClickButton (int id)

【讨论】:

  • 我明白了。但是,当我使用您的建议重构代码时,我面临的问题是我还需要重构我的事件,但我不知道该怎么做。例如,当使用“async Task HandleWidget_ClickButton (int id)”时,我绑定的代码 (_widget.ClickButton += HandleWidget_ClickButton) 给出了错误“Task HandleWidget_ClickButton has the wrong return type”。跨度>
【解决方案3】:

正如其他答案中提到的,您可以正确构建事件处理程序,以免引发此事件,因为运行时将知道如何正确调用委托。下面是一些代码,向您展示如何构建它:

向您的视图公开一个公共命令

public ICommand CopyDeviceId { get; private set; }

然后,创建一个带有委托的命令实例

CopyDeviceId = new Command(() =&gt; CopyDeviceIdValue(this, EventArgs.Empty));

最后,创建异步委托

private async void CopyDeviceIdValue(object sender, EventArgs e)
{
    \\code here
}

【讨论】:

    猜你喜欢
    • 2015-04-08
    • 2018-03-03
    • 2015-12-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多