C# 如何使用委托/回调编写相同的代码?

C# 如何使用委托/回调编写相同的代码?,c#,winforms,delegates,async-await,C#,Winforms,Delegates,Async Await,这里有一个非常简单的例子,可以在互联网上找到异步/等待概念,我想知道如何使用开始/结束调用实现这个小逻辑? 我所需要做的就是使用这两种方法(异步/等待和委托)准备一个复杂的逻辑,所以我想从一个基本的工作流开始 int countCharacters() { int count = 0; using (StreamReader reader= new StreamReader("D:\\Data.txt")) { s

这里有一个非常简单的例子,可以在互联网上找到异步/等待概念,我想知道如何使用开始/结束调用实现这个小逻辑? 我所需要做的就是使用这两种方法(异步/等待和委托)准备一个复杂的逻辑,所以我想从一个基本的工作流开始

int countCharacters()
    {

        int count = 0;
        using (StreamReader reader= new StreamReader("D:\\Data.txt"))
        {
            string content = reader.ReadToEnd();
            count = content.Length;
            Thread.Sleep(5000);
        }
            return count; 
    }
    private async void btnProcessFIle_Click(object sender, EventArgs e)
    {
        Task<int> task = new Task<int>(countCharacters);
        task.Start();
        int count = await task;
        lblCount.Text = "No. of characters in file=" +Environment.NewLine+ count.ToString();


    }
int countCharacters()
{
整数计数=0;
使用(StreamReader=newstreamreader(“D:\\Data.txt”))
{
字符串内容=reader.ReadToEnd();
计数=内容。长度;
睡眠(5000);
}
返回计数;
}
私有异步无效btnProcessFIle\u单击(对象发送方,事件参数e)
{
任务任务=新任务(countCharacters);
task.Start();
int count=等待任务;
lblCount.Text=“文件中的字符数=“+Environment.NewLine+count.ToString();
}

功能BeginInvoke可以访问非UI线程中的UI元素,因此您需要像这样更改代码:

    private async void btnProcessFIle_Click(object sender, EventArgs e)
{
    var task = Task.Run(() => {
     var count = countCharacters();
     lblCount.BeginInvoke((Action) (() =>
       {
         lblCount.Text = "No. of characters in file=" +Environment.NewLine+ 
            count.ToString();
       }));
    });

    await task;

}

没有异步/等待的代码。您不需要使用BeginInvoke,因为您可以使用
TaskScheduler.fromSynchronizationCurrentContext()

int countCharacters()
{
整数计数=0;
使用(StreamReader=newstreamreader(“D:\\Data.txt”))
{
字符串内容=reader.ReadToEnd();
计数=内容。长度;
睡眠(5000);
}
返回计数;
}
私有void btnProcessFIle\u单击(对象发送方,事件参数e)
{
任务任务=新任务(countCharacters);
task.Start();
var uiScheduler=TaskScheduler.FromCurrentSynchronizationContext();
task.ContinueWith(计数=>{
lblCount.Text=“文件中的字符数=“+Environment.NewLine+count.ToString();
},调度程序);
}
如果你真的想用BeginInvoke,我想你可以写

    private  void btnProcessFIle_Click(object sender, EventArgs e)
    {
        Task<int> task = new Task<int>(countCharacters);
        task.Start();
        var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
        task.ContinueWith(count =>{
                 lblCount.BeginInvoke(()=>{
                     lblCount.Text = "No. of characters in file=" +Environment.NewLine+ count.ToString();
                 });

        });
    }
private void btnProcessFIle\u单击(对象发送方,事件参数e)
{
任务任务=新任务(countCharacters);
task.Start();
var uiScheduler=TaskScheduler.FromCurrentSynchronizationContext();
task.ContinueWith(计数=>{
lblCount.BeginInvoke(()=>{
lblCount.Text=“文件中的字符数=“+Environment.NewLine+count.ToString();
});
});
}
这是一个非常简单的例子,可以在互联网上找到异步/等待的概念

这是一个非常糟糕的例子

它使用任务构造函数和
Start
,这是一个明确的禁忌(实际上没有有效的用例可以做到这一点)

在“异步示例”中,它还在后台线程上同步使用文件系统

如果您想要一个如何异步使用同步(例如,CPU绑定)代码的示例,那么这是一个更好的示例,它可以做几乎相同的事情:

int countCharacters()
{
  Thread.Sleep(5000);
  return 13;
}

private async void btnProcessFIle_Click(object sender, EventArgs e)
{
  var count = await Task.Run(() => countCharacters());
  lblCount.Text = "No. of characters in file=" + count;
}
请注意,这是一个如何从UI线程异步调用CPU绑定代码的示例—它不是“异步概念”的示例

我想知道如何使用Begin/End Invoke实现这个小逻辑

由于您的工作是在后台线程上进行同步工作,所以这实际上相当容易;不要创建自己的
IAsyncResult
(这是APM真正困难的部分-如果确实需要实现它,请参阅中的“实现CLR异步编程模型”)

但是,由于您可以只使用
Delegate.BeginInvoke
,因此非常简单:

private void btnProcessFIle_Click(object sender, EventArgs e)
{
  var ui = SynchronizationContext.Current;
  Func<int> d = countCharacters;
  d.BeginInvoke(CountCharactersCallback, ui);
}

private void CountCharactersCallback(IAsyncResult ar) 
{
  var d = (Func<int>) ((AsyncResult) ar).AsyncDelegate;
  var ui = (SynchronizationContext) ar.AsyncState;
  try
  {
    var count = d.EndInvoke(ar);
    ui.Post(CountCharactersComplete, count);
  }
  catch (Exception ex)
  {
    var edi = ExceptionDispatchInfo.Capture(ex);
    ui.Post(CountCharactersError, state);
  }
}

private void CountCharactersComplete(object state)
{
  var count = (int) state;
  lblCount.Text = "No. of characters in file=" + count;
}

private void CountCharactersError(object state)
{
  var edi = (ExceptionDispatchInfo)state;
  edi.Throw();
}
private void btnProcessFIle\u单击(对象发送方,事件参数e)
{
var ui=SynchronizationContext.Current;
Func d=可计数字符;
d、 BeginInvoke(CountCharactersCallback,ui);
}
私有void CountCharactersCallback(IAsyncResult ar)
{
var d=(Func)((AsyncResult)ar).AsyncDelegate;
var ui=(SynchronizationContext)ar.AsyncState;
尝试
{
var count=d.EndInvoke(ar);
ui.Post(CountCharactersComplete,count);
}
捕获(例外情况除外)
{
var edi=例外DispatchInfo.Capture(ex);
ui.Post(CountCharactersError,state);
}
}
私有void CountCharactersComplete(对象状态)
{
变量计数=(int)状态;
lblCount.Text=“文件中的字符数=”+计数;
}
私有void countCharacterError(对象状态)
{
var edi=(ExceptionDispatchInfo除外)状态;
edi.Throw();
}
注:

  • CountCharactersCallback
    是一个“裸回调”。从
    CountCharacterScalBack
    传播的任何异常都表示发生灾难性错误
  • 特别是,必须小心不要让
    EndInvoke
    的异常传播到
    BeginInvoke
    回调之外。这是一个常见的错误
  • 我正在使用
    SynchronizationContext
    同步回UI线程。这与等待的行为相同
  • 我使用
    ExceptionDispatchInfo
    跨线程保留异常堆栈跟踪(不需要包装异常)
  • countCharacterError
    只是直接在消息循环中引发异常。这与
    async void
    的行为相同

我既不想使用任务,也不想使用异步或等待。我想用这个委托、回调、BeginInvoke和EndInvoke来实现的全部功能。我的要求真的很高吗?@Eqra-Task和async/await是为实现TAP而创建的-如果您只想使用多线程-没有问题(但为什么?-在这种情况下,您的btnProcessFIle_Click()在等待任务结束时被“冻结”)—您可以在msdn中的“任务类”定义中找到的示例,OP并没有要求。问题不是如何更新UI。我好奇的是如何使用APM模型。为什么要使用非常好的异步/等待代码并将其转换为使用Begin/End Invoke?开始/结束调用是一种老方法
private void btnProcessFIle_Click(object sender, EventArgs e)
{
  var ui = SynchronizationContext.Current;
  Func<int> d = countCharacters;
  d.BeginInvoke(CountCharactersCallback, ui);
}

private void CountCharactersCallback(IAsyncResult ar) 
{
  var d = (Func<int>) ((AsyncResult) ar).AsyncDelegate;
  var ui = (SynchronizationContext) ar.AsyncState;
  try
  {
    var count = d.EndInvoke(ar);
    ui.Post(CountCharactersComplete, count);
  }
  catch (Exception ex)
  {
    var edi = ExceptionDispatchInfo.Capture(ex);
    ui.Post(CountCharactersError, state);
  }
}

private void CountCharactersComplete(object state)
{
  var count = (int) state;
  lblCount.Text = "No. of characters in file=" + count;
}

private void CountCharactersError(object state)
{
  var edi = (ExceptionDispatchInfo)state;
  edi.Throw();
}