目录

C#(WPF)-回调

一、什么是回调(Callback)

回调(Callback) 指的是:

把一个方法作为参数传递给另一个方法,在特定时机由被调用方反向调用该方法。

换句话说:

调用者 → 把函数传给被调用者 → 被调用者在合适时机执行这个函数

核心思想:

A 调用 B
B 在未来某个时间点 再调用 A 提供的方法

简单流程:

Main
 └── 调用 Process()
        └── 执行逻辑
        └── 调用 Callback()

二、回调的主要作用

作用 说明
异步通知 异步操作完成后通知调用者
事件驱动 响应事件(按钮点击、状态变化)
解耦合 调用者与执行逻辑分离
扩展性 不修改原代码即可扩展
自定义行为 把行为作为参数传递
管道处理 多个步骤连续处理数据

三、C#回调的实现方式

C#提供多种实现方式,本质都是函数引用

主要方式:

委托 Delegate
Action / Func
事件 Event
接口回调
传统异步 AsyncCallback
Task + async/await
回调链

四、委托(Delagate)回调

委托是C#回调的基础机制

1.定义委托

public delegate void ProcessCallback(string result);

含义:

一个指向方法的类型
要求方法签名:
void Method(string)

2.使用委托回调

public class Processor
{
    public void ProcessDate(string data, ProcessCallback callback)
    {
        Console.WriteLine($"处理数据:{data}");
        
        string result = $"已处理:{data}";
        
        callback?.Invoke(result);
    }
}

调用:

class Program
{
    static void Main()
    {
        var processor = new Processor();
        
        processor.ProcessData("测试数据", OnProcessComplete);
    }
    
    static void OnProcessComplete(string result)
    {
        Console.WriteLine($"回调结果");
    }
}

执行流程:

Main
ProcessData()
callback.Invoke()
OnProcessComplete()

五、Action / Func 泛型委托

.NET内置委托

优点:

  • 不需要自定义 delegate
  • 更简洁

1.Action(无返回值)

public void Process(string data, Action<string> callback)
{
    string result = $"$处理问题:{data}";
    callback?.Invoke(result);
}

调用:

processor.Process("数据", result =>
{
    Console.WriteLine(result);
});

2.Func(有返回值)

public int Transform(int x, Func<int, int> transform)
{
    return transform(x);
}

调用:

int result = Transform(5, x => x * 2);

3.Action / Func 规则

Action

Action
Action<T>
Action<T1, T2>

返回值:

void

Func

Func<T>
Func<T, TResult>
Func<T1, T2, TResult>

最后一个是返回值

例:

Func<int, int>
输入 int
返回 int

六、事件(Event) 回调

事件是基于委托的发布-订阅模式

常用于:

UI
状态通知
系统事件

定义事件

public class TemperatureMonitor
{
    public event Action<double> OnTemperatureChanged;
    
    private double _temperature;
    
    public double Temperature
    {
        get => _temperature;
        set
        {
            if (Math.Abs(_temperature - value) > 0.1)
            {
                _temperature = value;
                OnTemperatureChanged?.Invoke(value);
            }
        }
    }
}

订阅事件

monitor.OnTemperatureChanged += HandleTemperature;

monitor.OnTemperatureChanged += temp =>
{
    Console.WirteLine($"温度变化{temp}");
};

触发事件

monitor.Temperature = 25.5

执行:

OnTemperatureChanged
 ├── HandleTemperature
 └── Lambda

七、接口回调(Interface Callback)

适用于:

  • 回调方法很多

  • 需要回调协议

  • 需要扩展

定义接口

public interface IProcessCallback
{
    void OnComplete(string result);
    void OnError(Exception ex);
}

实现接口

public class LoggerCallback : IProcessCallback
{
    public void OnComplete(string result)
    {
        Console.WriteLine($"完成:{result}");
    }
    
    public void OnError(Exception ex)
    {
        Console.WriteLine($"错误:{ex.Message}");
    }
}

使用

public void Process(string data, IProcessCallback callback)
{
    try
    {
        callback.OnComplete("成功");
    }
    catch (Exception ex)
    {
        callback.OnError(ex);
    }
}

优点

结构清晰
支持多个回调方法
可扩展

八、传统异步回调(AsyncCallback)

旧版 .NET异步模式:

Asynchronous Programming Model

核心:

BeginXXX
EndXXX
AsyncCallback

示例:

processor.BeginProcess(data, callback, state);

回调:

void Callback(IAsyncResult ar)
{
    processor.EndProcess(ar);
}

但现在基本 已被 Task 替代

九、Task + async/await(现代异步)

异步方法

public async Task<string> ProcessAsync(string data)
{
    await Task.Delay(1000);
    return $"处理完成{data}";
}

ContinueWith回调

processor.ProcessAsync("数据")
    	.ContinueWith(t => 
        {
            Console.WriteLine(t.Result);
        });

async回调

await processor.ProcessWithCallbackAsync("数据", async result =>
{
    Console.WriteLine(result);
});

十、回调链(Callback Chain)

类似管道处理Pipeline。

public void ProcessChain(string data, params Action<string>[] callbacks)
{
    string current = data;
    
    foreach (var cb in callbacks)
    {
        cb(current);
        current = $"处理:{current}";
    }
}

调用:

chain.ProcessChain("原始",
    x => Console.WriteLine($"步骤1 {x}"),
    x => Console.WriteLine($"步骤2 {x}"),
    x => Console.WriteLine($"步骤3 {x}")
);

十一、实际开发选择

场景 推荐
简单回调 Action / Func
事件通知 event
复杂回调协议 interface
异步操作 async / await
链式处理 回调链
旧代码维护 AsynCallback

Demo:模拟一个大文件分析工具

功能:

  • 用户点击 开始分析
  • 后台线程 异步处理数据
  • 进度回调
  • 日志回调
  • 完成回调
  • 错误回调
  • UI自动更新(MVVM + INotifyPropertyChanged)
  • 取消操作
  • 事件回调 + 委托回调 + async/await 混合

项目结构:

WpfCallbackDemo
├── MainWindow.xaml
├── MainWindow.xaml.cs
├── ViewModels
│     └── MainViewModel.cs
├── Services
│     └── DataAnalyzer.cs
├── Callbacks
│     └── IAnalyzerCallback.cs
├── Utils
│     └── RelayCommand.cs

架构关系:

UI (WPF)
ViewModel
Service (DataAnalyzer)
   ├── 事件回调 Event
   ├── 委托回调 Action
   ├── 接口回调 Interface
   └── async/await
<Window
    x:Class="WpfCallbackDemo.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="clr-namespace:WpfCallbackDemo"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:vm="clr-namespace:WpfCallbackDemo.ViewModels"
    Title="Callback Analyzer"
    Width="500"
    Height="350"
    mc:Ignorable="d">

    <Window.DataContext>
        <vm:MainViewModel />
    </Window.DataContext>


    <Grid Margin="20">
        <StackPanel>

            <Button
                Height="40"
                Command="{Binding StartCommand}"
                Content="Start Analysis" />

            <Button
                Height="40"
                Margin="0,10,0,0"
                Command="{Binding CancelCommand}"
                Content="Cancel" />

            <ProgressBar
                Height="25"
                Margin="0,10,0,0"
                Maximum="100"
                Value="{Binding Progress}" />

            <ListBox
                Height="150"
                Margin="0,10,0,0"
                ItemsSource="{Binding Logs}" />

        </StackPanel>
    </Grid>
</Window>
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using WpfCallbackDemo.Callbacks;
using WpfCallbackDemo.Services;
using WpfCallbackDemo.Utils;

namespace WpfCallbackDemo.ViewModels
{
    public class MainViewModel : INotifyPropertyChanged, IAnalyzerCallback
    {
        private readonly DataAnalyzer analyzer = new DataAnalyzer();
        private CancellationTokenSource cts;

        public ObservableCollection<string> Logs { get; } = new();

        private int progress;
        public int Progress
        {
            get => progress;
            set
            {
                if (progress != value)
                {
                    progress = value;
                    OnPropertyChanged();
                }
            }
        }


        private string status;
        public string Status
        {
            get => status;
            set
            {
                if (status != value)
                {
                    status = value;
                    OnPropertyChanged();
                }
            }
        }

        public RelayCommand StartCommand { get; }
        public RelayCommand CancelCommand { get; }

        public MainViewModel()
        {
            StartCommand = new RelayCommand(async _ => await Start());
            CancelCommand = new RelayCommand(_ => Cancel());

            analyzer.ProgressChanged += OnProgressChanged;
        }

        private async Task Start()
        {
            Logs.Clear();
            cts = new CancellationTokenSource();

            await analyzer.AnalyzeAsync(
                "datafile.txt",
                log => Logs.Add(log), // 委托回调
                this, // 接口回调
                cts.Token);
        }

        private void Cancel()
        {
            cts?.Cancel();
        }

        // Event 回调
        private void OnProgressChanged(int value)
        {
            Progress = value;
            Logs.Add($"Progress: {value}%");
        }

        // 接口回调
        public void OnCompleted(string result)
        {
            Status = result;
            Logs.Add("Analysis completed successfully.");
        }

        public void OnError(string error)
        {
            Status = error;
        }

        public event PropertyChangedEventHandler? PropertyChanged;

        private void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}
using WpfCallbackDemo.Callbacks;

namespace WpfCallbackDemo.Services
{
    public class DataAnalyzer
    {
        // Event 回调
        public event Action<int> ProgressChanged;

        public async Task AnalyzeAsync(
            string file,
            Action<string> logCallback,
            IAnalyzerCallback callback,
            CancellationToken token)
        {
            try
            {
                for (int i = 0; i <= 10; i++)
                {
                    token.ThrowIfCancellationRequested();

                    await Task.Delay(500);

                    ProgressChanged?.Invoke(i * 10);

                    logCallback?.Invoke($"Processing block {i}");
                }

                string result = $"Analysis Completed: {file}";

                callback.OnCompleted(result);
            }
            catch (OperationCanceledException)
            {
                callback.OnError("Operation cancelled");
            }
            catch (Exception ex)
            {

                callback.OnError(ex.Message);
            }
        }
    }
}
namespace WpfCallbackDemo.Callbacks
{
    public interface IAnalyzerCallback
    {
        void OnCompleted (string result);
        void OnError (string ex);
    }
}

执行流程:

Button
Command
ViewModel.Start()
DataAnalyzer.AnalyzeAsync()

产生三种回调

1. 事件回调 Event

Service
ProgressChanged Event
ViewModel.OnProgressChanged
UI ProgressBar 更新

2. 委托回调 Action

Service
logCallback.Invoke()
ViewModel Logs.Add()
UI ListBox 更新

3. 接口回调 Interface

Service
callback.OnCompleted()
ViewModel.OnCompleted()
UI Status 更新

为什么WPF非常依赖回调?

WPF本质是 事件驱动 + 异步架构

UI不能阻塞:必须保持流程

所以必须:后台线程处理,回调通知UI.