回调:通常是指,在另一个方法完成某项操作后调用某个方法。
下面是常见的回调实现方式
1.委托和事件
委托回调:委托是一个类型安全的函数指针,可以绑定多个方法
public delegate void MyCallback(string msg)
class Program
{
static void Main()
{
MyCallback myCallback = new MyCallback(CallbackMethod);
ExecuteTask(myCallback);
}
static void ExecuteTask(MyCallback callback)
{
//做某些操作
Console.WriteLine("Task completed");
callback("execute callback method");
}
static void CallbackMethod(string message)
{
Console.WriteLine($"Callback received: {message}");
}
}
那么就有人要问了,下面这两种调用有区别吗?
//调用方式一
static void ExecuteTask(MyCallback callback)
{
// 模拟一些任务完成后的回调
Console.WriteLine("Task completed!");
callback("Task is complete!");
}
//调用方式二
static void ExecuteTask(MyCallback callback)
{
// 模拟一些任务完成后的回调
Console.WriteLine("Task completed!");
Program.CallbackMethod("Task is complete!");
}
我的回答是:有的兄弟,有的
callback是一个委托,它具有一定的灵活性,即,如果我们在创建这个实例时,通过给他传递不同的方法,可以在ExecuteTask里执行不同的方法
static void Main()
{
MyCallback myCallback = new MyCallback(CallbackMethod);
ExecuteTask(myCallback);
MyCallback myCallback2 = new MyCallback(DontCallbackMethod);
ExecuteTask(myCallback2);
}
回调方法可以是外部方法,可以传入任何符合MyCallback委托签名的方法,包括在外部类中的方法
事件回调: 基于委托的一种更高级的机制,用于实现发布/订阅模式,事件可以通知订阅者某个操作的实现
public class Publisher
{
public event Action<string> OnCompleted;
public void ExecuteTask()
{
//模拟任务执行
Console.WriteLine("task is in progress...");
//任务完成,通知订阅者,并传递信息"task completed!"
OnCompleted?.Invoke("task completed!");
}
}
class program
{
static void Main()
{
Publisher publisher = new Publisher();
publisher.OnCompleted += CallbackMethod; //订阅类里的事件
publisher.ExecuteTask(); //执行该方法,当执行到invoke那一步的时候就通知订阅者
}
static void CallbackMethod(string obj)
{
Console.WriteLine($"callback reveived:{obj}");
}
}
2.UI线程回调(Invoke)
using System;
using System.Windows.Forms;
class Program
{
static void Main()
{
Form form = new Form();
Button button = new Button();
button.Text = "Click Me!";
button.Click += Button_Click;
form.Controls.Add(button);
Application.Run(form);
}
private static void Button_Click(object sender, EventArgs e)
{
//是否是UI线程操作
if (sender is Control control && control.InvokeRequired)
{
//启用Ui线程执行该方法
control.Invoke(new Action(() => MessageBox.Show("Button clicked!")));
}
else
{
MessageBox.Show("Button clicked!");
}
}
}
3. Task和async/await 异步回调 最常用
async和await 可以取代大部分的回调机制
***NOTE***
async/await是Task的语法糖,他们虽然不创建新的线程,但是可以把当前线程挂起,让CPU去执行其他事情,让异步执行起来和同步一样。
Task是基于线程池(ThreadPool)的,通过Task.run()提交任务时,会在线程池中运行。
async/await又是基于回调机制的,当在方法中使用await语句,await会在任务未完成是注册一个回调。当任务完成是,回来继续执行方法后续的代码。
using System;
using System.Threading;
using System.Threading.Tasks;
class program
{
static async void Main()
{
await ExecuteTaskAsync();
ExecuteTask();
}
private static void ExecuteTask()
{
Console.WriteLine("task started");
Thread.Sleep(1000);
Console.WriteLine("task completed");
DirectMethod("Task is complete!");
}
private static void DirectMethod(string v)
{
Console.WriteLine($"received:{v}");
}
static async Task ExecuteTaskAsync()
{
Console.WriteLine("task started");
await TimeConsumTask(1000); //模拟异步操作
Console.WriteLine("task completed");
CallbackMethod("Task is complete!");
}
private static async Task TimeConsumTask(int v)
{
await Task.Delay(v);
}
private static void CallbackMethod(string v)
{
Console.WriteLine($"callback received:{v}");
}
}
4. Action和Func (内联委托)
Action
和Func
是C#内置的委托类型,Action
用于没有返回值的回调,Func
用于有返回值的回调。
Action回调
class Program
{
static void Main()
{
Action<string> callback = message => Console.WriteLine($"Callback received: {message}");
ExecuteTask(callback);
}
static void ExecuteTask(Action<string> callback)
{
Console.WriteLine("Task completed!");
callback("Task is complete!");
}
}
//本质上下面这个没有区别的
class Program3
{
static void Main()
{
MyCallback myCallback = new MyCallback(CallbackMethod);
ExecuteTask(myCallback);
}
static void ExecuteTask(MyCallback callback)
{
Console.WriteLine("Task completed");
callback("Task is complete!");
}
static void CallbackMethod(string message)
{
Console.WriteLine($"Callback received: {message}");
}
}
Func回调和Action回调的差别就是有返回值