还在为复杂的业务逻辑写出一堆嵌套代码而头疼吗?还在为方法调用层层套娃而苦恼吗?今天就来聊聊C#中的**链式编程(Method Chaining)**这个神器!
想象一下,原本需要十几行代码才能完成的设备连接、数据采集、导出操作,现在只需要几行流畅的链式调用就能搞定。不仅代码更简洁,逻辑更清晰,维护成本也大大降低。
本文将通过一个设备数据采集系统的完整案例,带你掌握链式编程的精髓,让你的C#代码从此告别"意大利面条式"的混乱!
在日常开发中,我们经常遇到这样的场景:
C#// 传统写法:冗长且容易出错
var client = new DeviceClient();
client.Setup("192.168.1.100", 502);
client.OnLog(msg => Console.WriteLine(msg));
if (client.Connect())
{
client.Collect(10);
var data = client.GetCollectedData();
if (data.Count > 0)
{
client.ExportData("Excel", @"C:\data\export.xlsx");
}
client.Disconnect();
}
问题显而易见:
看看用链式编程改造后的效果:
C#// 链式写法:简洁优雅
client.Setup("192.168.1.100", 502)
.OnLog(msg => Console.WriteLine($"[LOG] {msg}"))
.Connect()
.Collect(10)
.OnDataCollected((count, avg) =>
Console.WriteLine($"采集完成:{count}条,平均值:{avg:F2}"))
.ExportData("Excel", @"C:\data\export.xlsx")
.Disconnect();
立竿见影的改善:
链式编程的秘诀在于方法返回自身实例:
C#public class DeviceClient
{
private string ip;
private int port;
private bool connected;
private Action<string> logger;
// 💡 关键:每个方法都返回 this
public DeviceClient Setup(string ip, int port)
{
this.ip = ip;
this.port = port;
return this; // 🔥 链式编程的核心
}
public DeviceClient OnLog(Action<string> logger)
{
this.logger = logger;
return this; // 返回自身,支持继续链式调用
}
public DeviceClient Connect()
{
connected = true;
logger?.Invoke($"已连接到 {ip}:{port}");
return this;
}
}
让我们通过一个完整的设备数据采集系统来深入理解链式编程的实际应用:
C#public class DataPoint
{
public int Id { get; set; }
public DateTime Timestamp { get; set; }
public double Value { get; set; }
public double Temperature { get; set; }
public double Pressure { get; set; }
public string DeviceIp { get; set; }
public string Status { get; set; }
public string Operator { get; set; }
}
C#public DeviceClient Collect(int count)
{
if (!connected)
{
logger?.Invoke("未连接设备。");
return this;
}
collectedData.Clear();
Random rand = new Random();
for (int i = 0; i < count; i++)
{
var value = rand.Next(0, 100);
var temperature = Math.Round(rand.NextDouble() * 50 + 20, 2);
var pressure = Math.Round(rand.NextDouble() * 10 + 1, 2);
var dataPoint = new DataPoint
{
Id = i + 1,
Timestamp = DateTime.Now.AddSeconds(i),
Value = value,
Temperature = temperature,
Pressure = pressure,
DeviceIp = ip,
Status = value > 50 ? "正常" : "警告",
Operator = Environment.UserName
};
collectedData.Add(dataPoint);
logger?.Invoke($"采集数据[{i + 1}]:Value={value}, Status={dataPoint.Status}");
}
return this; // 🔥 链式调用的关键
}
C#// 数据采集完成回调
public DeviceClient OnDataCollected(Action<int, double> callback)
{
if (collectedData.Count > 0)
{
var avgValue = collectedData.Average(d => d.Value);
callback?.Invoke(collectedData.Count, avgValue);
}
return this;
}
C#public DeviceClient ExportData(string format, string filePath)
{
if (collectedData.Count == 0)
{
logger?.Invoke("没有可导出的数据。");
return this;
}
try
{
// 自动生成文件名
if (string.IsNullOrEmpty(filePath))
{
var extension = format == "Excel" ? "xlsx" : "csv";
filePath = $"数据导出_{DateTime.Now:yyyyMMdd_HHmmss}.{extension}";
}
if (format == "Excel")
ExportToExcel(filePath);
else
ExportToCsv(filePath);
logger?.Invoke($"数据已成功导出到: {filePath}");
}
catch (Exception ex)
{
logger?.Invoke($"导出失败: {ex.Message}");
}
return this;
}
使用ClosedXML库实现专业级Excel导出:
C#private void ExportToExcel(string filePath)
{
using (var workbook = new XLWorkbook())
{
var worksheet = workbook.Worksheets.Add("数据采集记录");
// 🎨 精美的表头设计
var headers = new[] { "序号", "采集时间", "数值", "温度(°C)",
"压力(bar)", "设备IP", "状态", "操作员" };
for (int i = 0; i < headers.Length; i++)
{
var cell = worksheet.Cell(1, i + 1);
cell.Value = headers[i];
cell.Style.Font.Bold = true;
cell.Style.Fill.BackgroundColor = XLColor.LightBlue;
cell.Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center;
}
// 📊 数据行的智能着色
for (int row = 0; row < collectedData.Count; row++)
{
var data = collectedData[row];
var excelRow = row + 2;
// 填充数据...
// 🚨 状态着色:警告=黄色,正常=绿色
if (data.Status == "警告")
worksheet.Row(excelRow).Style.Fill.BackgroundColor = XLColor.LightYellow;
else if (data.Status == "正常")
worksheet.Row(excelRow).Style.Fill.BackgroundColor = XLColor.LightGreen;
}
// 📈 自动生成统计信息
AddStatisticsSection(worksheet, collectedData.Count + 2);
workbook.SaveAs(filePath);
}
}
在WinForms中使用链式编程同样优雅:
C#namespace AppChainApp
{
public partial class Form1 : Form
{
DeviceClient client;
public Form1()
{
InitializeComponent();
client = new DeviceClient();
// UI绑定
client = new DeviceClient();
// UI绑定
btnConnect.Click += (s, e) => ConnectDevice();
btnCollect.Click += (s, e) => CollectData();
btnDisconnect.Click += (s, e) => DisconnectDevice();
btnExport.Click += (s, e) => ExportData();
}
private void ConnectDevice()
{
txtLog.Clear();
try
{
client
.Setup(txtIp.Text, int.Parse(txtPort.Text))
.OnLog(msg => txtLog.AppendText("[连接] " + msg + Environment.NewLine))
.Connect();
}
catch (Exception ex)
{
txtLog.AppendText("[异常] " + ex.Message + Environment.NewLine);
}
}
private void CollectData()
{
try
{
client
.OnLog(msg => txtLog.AppendText("[采集] " + msg + Environment.NewLine))
.Collect(int.Parse(txtCount.Text))
.OnDataCollected((count, avgValue) =>
{
txtLog.AppendText($"[统计] 共采集 {count} 条数据,平均值: {avgValue:F2}" + Environment.NewLine);
});
}
catch (Exception ex)
{
txtLog.AppendText("[异常] " + ex.Message + Environment.NewLine);
}
}
private void DisconnectDevice()
{
try
{
client
.OnLog(msg => txtLog.AppendText("[断开] " + msg + Environment.NewLine))
.Disconnect();
}
catch (Exception ex)
{
txtLog.AppendText("[异常] " + ex.Message + Environment.NewLine);
}
}
// 导出功能
private void ExportData()
{
try
{
var exportFormat = rbExcel.Checked ? "Excel" : "CSV";
client
.OnLog(msg => txtLog.AppendText("[导出] " + msg + Environment.NewLine))
.ExportData(exportFormat, txtFilePath.Text);
}
catch (Exception ex)
{
txtLog.AppendText("[异常] " + ex.Message + Environment.NewLine);
}
}
// 浏览文件路径
private void btnBrowse_Click(object sender, EventArgs e)
{
using (SaveFileDialog sfd = new SaveFileDialog())
{
sfd.Filter = "Excel文件|*.xlsx|CSV文件|*.csv";
sfd.DefaultExt = rbExcel.Checked ? "xlsx" : "csv";
if (sfd.ShowDialog() == DialogResult.OK)
{
txtFilePath.Text = sfd.FileName;
}
}
}
}
}
C#// ❌ 错误:忘记返回this,链式调用中断
public DeviceClient Connect()
{
connected = true;
// 缺少 return this;
}
// ✅ 正确:始终返回this
public DeviceClient Connect()
{
connected = true;
return this; // 必须返回自身
}
C#// ✅ 推荐:在每个方法内部处理异常,保证链式调用的连续性
public DeviceClient Connect()
{
try
{
// 连接逻辑
connected = true;
logger?.Invoke($"已连接到 {ip}:{port}");
}
catch (Exception ex)
{
logger?.Invoke($"连接失败: {ex.Message}");
// 不抛出异常,保证链式调用继续
}
return this;
}
C#// ✅ 防御性编程:始终检查必要的前置条件
public DeviceClient Collect(int count)
{
if (!connected)
{
logger?.Invoke("未连接设备。");
return this; // 即使条件不满足,也要返回this
}
// 执行采集逻辑...
return this;
}
C#// 🎯 通用链式编程模板
public class ChainableClass
{
// 配置方法
public ChainableClass Configure(/* 参数 */)
{
// 配置逻辑
return this;
}
// 行为方法
public ChainableClass Execute(/* 参数 */)
{
// 执行逻辑
return this;
}
// 回调方法
public ChainableClass OnComplete(Action<T> callback)
{
// 回调逻辑
return this;
}
}
通过本文的设备数据采集系统案例,我们深入探索了C#链式编程的三个核心要点:
1. 核心机制:每个方法返回this
,实现流畅的方法链调用
2. 实战应用:从设备连接到数据导出的完整业务流程,代码简洁性提升50%+
3. 最佳实践:异常处理、空引用检查、防御性编程,确保链式调用的稳定性
链式编程不仅让代码更优雅,更重要的是改变了我们的编程思维方式。它将复杂的业务流程转化为自然语言般的表达,让代码的可读性和维护性得到质的飞跃。
💬 互动时间:
觉得有用请转发给更多同行,让我们一起写出更优雅的C#代码!🚀
相关信息
通过网盘分享的文件:AppChainApp.zip 链接: https://pan.baidu.com/s/1m4euYAhKVjtJLVUDHUPjeQ?pwd=7n28 提取码: 7n28 --来自百度网盘超级会员v9的分享
本文作者:技术老小子
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!