以下为C#高级编程中几个核心知识点的更详细代码示例,结合上位机开发场景,涵盖反射、接口与抽象类、泛型、委托与事件、多线程与异步编程、设计模式(单例与工厂)以及一个完整的上位机温度监控系统实战示例。每个示例都包含详细注释、异常处理和上位机开发中的实际应用场景,旨在帮助您深入理解并快速应用到实际项目中。
1. 反射(Reflection)
详细代码示例
csharp
using System;
using System.Reflection;
// 设备配置类
public class DeviceConfig
{
public string Name { get; set; }
public int Id { get; set; }
public bool IsActive { get; set; }
public void Start() => Console.WriteLine($"{Name} 启动");
}
// 反射管理器
public class DeviceReflectionManager
{
// 动态创建对象
public static object CreateInstance(string typeName)
{
try
{
Type type = Type.GetType(typeName) ?? throw new ArgumentException($"类型 {typeName} 未找到");
return Activator.CreateInstance(type);
}
catch (Exception ex)
{
Console.WriteLine($"创建实例失败: {ex.Message}");
return null;
}
}
// 动态设置属性
public static void SetProperty(object instance, string propertyName, object value)
{
try
{
PropertyInfo prop = instance.GetType().GetProperty(propertyName)
?? throw new ArgumentException($"属性 {propertyName} 未找到");
prop.SetValue(instance, value);
}
catch (Exception ex)
{
Console.WriteLine($"设置属性失败: {ex.Message}");
}
}
// 动态调用方法
public static void InvokeMethod(object instance, string methodName)
{
try
{
MethodInfo method = instance.GetType().GetMethod(methodName)
?? throw new ArgumentException($"方法 {methodName} 未找到");
method.Invoke(instance, null);
}
catch (Exception ex)
{
Console.WriteLine($"调用方法失败: {ex.Message}");
}
}
}
class Program
{
static void Main()
{
// 动态创建 DeviceConfig 实例
object device = DeviceReflectionManager.CreateInstance("DeviceConfig");
if (device == null) return;
// 动态设置属性
DeviceReflectionManager.SetProperty(device, "Name", "Sensor1");
DeviceReflectionManager.SetProperty(device, "Id", 101);
DeviceReflectionManager.SetProperty(device, "IsActive", true);
// 验证属性
DeviceConfig config = (DeviceConfig)device;
Console.WriteLine($"设备: {config.Name}, ID: {config.Id}, 状态: {config.IsActive}");
// 动态调用方法
DeviceReflectionManager.InvokeMethod(device, "Start");
}
}
上位机应用
场景:上位机需要动态加载设备驱动(如从DLL或配置文件中读取类型名)。
实现:通过反射创建设备实例,设置配置(如设备地址、通信协议),并调用其方法(如Start、Stop)。
优点:支持插件化开发,扩展性强。
注意:反射性能开销较大,建议缓存Type和PropertyInfo对象。
2. 接口与抽象类
详细代码示例
csharp
using System;
// 定义通信接口
public interface ICommunication
{
void Connect();
void Disconnect();
string ReadData();
}
// 抽象基类,包含通用逻辑
public abstract class BaseDevice
{
protected string DeviceName { get; set; }
protected bool IsConnected { get; private set; }
public void Log(string message) => Console.WriteLine($"[{DeviceName}] {message}");
public void SetConnectionStatus(bool status)
{
IsConnected = status;
Log($"连接状态: {(status ? "已连接" : "已断开")}");
}
}
// 串口通信实现
public class SerialDevice : BaseDevice, ICommunication
{
public SerialDevice(string name) => DeviceName = name;
public void Connect()
{
SetConnectionStatus(true);
Log("串口连接成功");
}
public void Disconnect()
{
SetConnectionStatus(false);
Log("串口断开");
}
public string ReadData()
{
if (!IsConnected) throw new InvalidOperationException("设备未连接");
return "温度: 25.5°C";
}
}
// TCP通信实现
public class TcpDevice : BaseDevice, ICommunication
{
public TcpDevice(string name) => DeviceName = name;
public void Connect()
{
SetConnectionStatus(true);
Log("TCP连接成功");
}
public void Disconnect()
{
SetConnectionStatus(false);
Log("TCP断开");
}
public string ReadData()
{
if (!IsConnected) throw new InvalidOperationException("设备未连接");
return "压力: 1.2bar";
}
}
class Program
{
static void Main()
{
ICommunication serial = new SerialDevice("传感器1");
ICommunication tcp = new TcpDevice("传感器2");
// 统一调用接口方法
serial.Connect();
Console.WriteLine(serial.ReadData());
serial.Disconnect();
tcp.Connect();
Console.WriteLine(tcp.ReadData());
tcp.Disconnect();
}
}
上位机应用
场景:上位机需要支持多种通信协议(如串口、TCP、Modbus)。
实现:定义ICommunication接口,抽象出通用方法;使用抽象类BaseDevice共享日志和状态管理逻辑。
优点:代码解耦,易于扩展新协议。
注意:确保接口方法设计合理,避免过于复杂的契约。
3. 泛型
详细代码示例
csharp
using System;
using System.Collections.Generic;
// 泛型数据缓冲区,支持约束
public class DataBuffer where T : struct
{
private readonly List _buffer;
private readonly int _maxSize;
public DataBuffer(int maxSize = 100)
{
_maxSize = maxSize;
_buffer = new List(maxSize);
}
public void Add(T item)
{
if (_buffer.Count >= _maxSize)
{
_buffer.RemoveAt(0); // 移除最早的数据
}
_buffer.Add(item);
}
public T Get(int index)
{
if (index < 0 || index >= _buffer.Count)
throw new ArgumentOutOfRangeException(nameof(index));
return _buffer[index];
}
public int Count => _buffer.Count;
public void Clear() => _buffer.Clear();
}
// 数据处理器
public class DataProcessor
{
public static double CalculateAverage(DataBuffer buffer) where T : struct
{
if (buffer.Count == 0) return 0;
double sum = 0;
for (int i = 0; i < buffer.Count; i++)
{
sum += Convert.ToDouble(buffer.Get(i));
}
return sum / buffer.Count;
}
}
class Program
{
static void Main()
{
DataBuffer tempBuffer = new DataBuffer(3);
tempBuffer.Add(25.5);
tempBuffer.Add(26.0);
tempBuffer.Add(25.8);
Console.WriteLine($"缓冲区数据量: {tempBuffer.Count}");
Console.WriteLine($"平均温度: {DataProcessor.CalculateAverage(tempBuffer):F2}°C");
// 添加超过最大容量的数据
tempBuffer.Add(27.0);
Console.WriteLine($"最新数据: {tempBuffer.Get(tempBuffer.Count - 1)}");
}
}
上位机应用
场景:上位机需要缓存不同类型的数据(如温度double、压力float)。
实现:使用泛型DataBuffer
优点:类型安全,代码复用性高。
注意:泛型约束(如where T : struct)确保数据类型符合需求。
4. 委托与事件
详细代码示例
csharp
using System;
using System.Threading;
// 数据接收处理器委托
public delegate void DataReceivedHandler(T data, DateTime timestamp);
// 数据采集器
public class DataCollector
{
public event DataReceivedHandler OnDataReceived;
public void StartCollecting()
{
try
{
while (true)
{
// 模拟采集数据
double data = new Random().NextDouble() * 100;
OnDataReceived?.Invoke(data, DateTime.Now);
Thread.Sleep(1000); // 每秒采集一次
}
}
catch (Exception ex)
{
Console.WriteLine($"采集错误: {ex.Message}");
}
}
}
// 数据处理器
public class DataProcessor
{
public void Subscribe(DataCollector collector)
{
// 使用Lambda表达式订阅事件
collector.OnDataReceived += (data, timestamp) =>
{
Console.WriteLine($"[{timestamp:HH:mm:ss}] 收到数据: {data:F2}");
if (data > 80)
Console.WriteLine("警告: 数据超出阈值!");
};
}
}
class Program
{
static void Main()
{
DataCollector collector = new DataCollector();
DataProcessor processor = new DataProcessor();
processor.Subscribe(collector);
// 在新线程中启动采集
Thread thread = new Thread(collector.StartCollecting);
thread.Start();
Console.ReadLine(); // 保持程序运行
}
}
上位机应用
场景:上位机需要实时处理传感器数据并触发报警。
实现:使用事件通知数据到达,处理器根据数据值执行逻辑(如报警、记录)。
优点:解耦采集与处理逻辑,支持多个订阅者。
注意:确保事件触发时线程安全,避免UI线程阻塞。
5. 多线程与异步编程
详细代码示例
csharp
using System;
using System.IO.Ports;
using System.Threading.Tasks;
public class SerialDataReader
{
private readonly SerialPort _port;
private bool _isRunning;
public SerialDataReader(string portName, int baudRate)
{
_port = new SerialPort(portName, baudRate)
{
ReadTimeout = 500
};
}
public event Action OnDataReceived;
public async Task StartReadingAsync(CancellationToken token)
{
try
{
_port.Open();
_isRunning = true;
while (_isRunning && !token.IsCancellationRequested)
{
try
{
// 异步读取串口数据
string data = await Task.Run(() => _port.ReadLine(), token);
OnDataReceived?.Invoke(data);
}
catch (TimeoutException)
{
// 忽略超时,继续读取
continue;
}
}
}
catch (Exception ex)
{
Console.WriteLine($"串口读取错误: {ex.Message}");
}
finally
{
if (_port.IsOpen) _port.Close();
}
}
public void Stop() => _isRunning = false;
}
class Program
{
static async Task Main()
{
SerialDataReader reader = new SerialDataReader("COM1", 9600);
CancellationTokenSource cts = new CancellationTokenSource();
reader.OnDataReceived += data => Console.WriteLine($"收到: {data}");
// 启动异步读取
Task readingTask = reader.StartReadingAsync(cts.Token);
Console.WriteLine("按任意键停止...");
Console.ReadKey();
// 停止读取
cts.Cancel();
reader.Stop();
await readingTask;
}
}
上位机应用
场景:上位机通过串口实时读取传感器数据,保持UI响应性。
实现:使用async/await异步读取串口数据,CancellationToken支持优雅停止。
优点:避免阻塞主线程,支持取消操作。
注意:处理串口异常(如端口被占用、超时),确保资源释放。
6. 设计模式(单例与工厂)
详细代码示例
csharp
using System;
using System.Collections.Generic;
// 通信接口
public interface ICommunication
{
void SendData(string data);
}
// 串口通信
public class SerialCommunication : ICommunication
{
public void SendData(string data) => Console.WriteLine($"串口发送: {data}");
}
// TCP通信
public class TcpCommunication : ICommunication
{
public void SendData(string data) => Console.WriteLine($"TCP发送: {data}");
}
// 单例模式:通信管理器
public class CommunicationManager
{
private static CommunicationManager _instance;
private static readonly object _lock = new object();
private CommunicationManager() { }
public static CommunicationManager Instance
{
get
{
lock (_lock)
{
return _instance ??= new CommunicationManager();
}
}
}
private readonly Dictionary _comms = new Dictionary();
public void Register(string key, ICommunication comm)
{
_comms[key] = comm ?? throw new ArgumentNullException(nameof(comm));
}
public ICommunication Get(string key)
{
return _comms.TryGetValue(key, out var comm) ? comm : throw new KeyNotFoundException($"未找到 {key}");
}
}
// 工厂模式:通信创建器
public class CommunicationFactory
{
public static ICommunication CreateCommunication(string type)
{
return type.ToLower() switch
{
"serial" => new SerialCommunication(),
"tcp" => new TcpCommunication(),
_ => throw new ArgumentException($"未知通信类型: {type}")
};
}
}
class Program
{
static void Main()
{
// 使用工厂创建通信实例
ICommunication serial = CommunicationFactory.CreateCommunication("serial");
ICommunication tcp = CommunicationFactory.CreateCommunication("tcp");
// 注册到单例管理器
CommunicationManager manager = CommunicationManager.Instance;
manager.Register("serial", serial);
manager.Register("tcp", tcp);
// 使用通信
manager.Get("serial").SendData("Hello Serial");
manager.Get("tcp").SendData("Hello TCP");
}
}
上位机应用
单例模式:确保上位机中只有一个通信管理器,统一管理所有设备连接。
工厂模式:根据配置文件或用户选择动态创建通信实例(如串口或TCP)。
优点:单例保证全局唯一,工厂增强扩展性。
注意:工厂模式中应避免硬编码,考虑使用配置文件或反射动态加载。
7. 上位机实战:温度监控系统
完整代码示例
以下是一个简化的WPF上位机程序,用于通过串口读取温度数据,实时显示曲线,并支持数据保存到SQLite数据库。
项目结构
MainWindow.xaml:WPF界面,显示温度曲线和当前值。
TemperatureMonitor.cs:处理串口通信和数据采集。
DataBuffer.cs:泛型数据缓冲区。
DatabaseHelper.cs:SQLite数据存储。
代码
csharp
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO.Ports;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using LiveCharts;
using LiveCharts.Wpf;
using System.Data.SQLite;
// 泛型数据缓冲区
public class DataBuffer where T : struct
{
private readonly List _buffer = new List();
private readonly int _maxSize;
public DataBuffer(int maxSize) => _maxSize = maxSize;
public void Add(T item)
{
if (_buffer.Count >= _maxSize) _buffer.RemoveAt(0);
_buffer.Add(item);
}
public IReadOnlyList GetAll() => _buffer.AsReadOnly();
}
// SQLite数据库帮助类
public class DatabaseHelper
{
private readonly string _connectionString;
public DatabaseHelper(string dbPath)
{
_connectionString = $"Data Source={dbPath};Version=3;";
InitializeDatabase();
}
private void InitializeDatabase()
{
using (var conn = new SQLiteConnection(_connectionString))
{
conn.Open();
string sql = @"CREATE TABLE IF NOT EXISTS Temperature (
Id INTEGER PRIMARY KEY AUTOINCREMENT,
Value REAL NOT NULL,
Timestamp TEXT NOT NULL)";
using (var cmd = new SQLiteCommand(sql, conn))
{
cmd.ExecuteNonQuery();
}
}
}
public void SaveTemperature(double value, DateTime timestamp)
{
using (var conn = new SQLiteConnection(_connectionString))
{
conn.Open();
string sql = "INSERT INTO Temperature (Value, Timestamp) VALUES (@value, @timestamp)";
using (var cmd = new SQLiteCommand(sql, conn))
{
cmd.Parameters.AddWithValue("@value", value);
cmd.Parameters.AddWithValue("@timestamp", timestamp.ToString("yyyy-MM-dd HH:mm:ss"));
cmd.ExecuteNonQuery();
}
}
}
}
// 温度监控类
public class TemperatureMonitor
{
private readonly SerialPort _port;
private readonly DataBuffer _buffer;
private readonly DatabaseHelper _db;
private bool _isRunning;
public event Action OnTemperatureReceived;
public TemperatureMonitor(string portName, int baudRate, string dbPath)
{
_port = new SerialPort(portName, baudRate) { ReadTimeout = 500 };
_buffer = new DataBuffer(100);
_db = new DatabaseHelper(dbPath);
}
public async Task StartAsync(CancellationToken token)
{
try
{
_port.Open();
_isRunning = true;
while (_isRunning && !token.IsCancellationRequested)
{
try
{
string data = await Task.Run(() => _port.ReadLine(), token);
if (double.TryParse(data, out double temp))
{
_buffer.Add(temp);
_db.SaveTemperature(temp, DateTime.Now);
OnTemperatureReceived?.Invoke(temp);
}
}
catch (TimeoutException) { continue; }
}
}
catch (Exception ex)
{
MessageBox.Show($"串口错误: {ex.Message}");
}
finally
{
if (_port.IsOpen) _port.Close();
}
}
public IReadOnlyList GetHistoryData() => _buffer.GetAll();
public void Stop() => _isRunning = false;
}
// ViewModel(MVVM模式)
public class MainViewModel : INotifyPropertyChanged
{
private double _currentTemperature;
public ChartValues TemperatureValues { get; } = new ChartValues();
public TemperatureMonitor Monitor { get; }
public double CurrentTemperature
{
get => _currentTemperature;
set
{
_currentTemperature = value;
OnPropertyChanged();
}
}
public MainViewModel()
{
Monitor = new TemperatureMonitor("COM1", 9600, "temperature.db");
Monitor.OnTemperatureReceived += temp =>
{
CurrentTemperature = temp;
TemperatureValues.Add(temp);
if (TemperatureValues.Count > 100) TemperatureValues.RemoveAt(0);
};
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
// WPF主窗口(MainWindow.xaml.cs)
public partial class MainWindow : Window
{
private readonly MainViewModel _viewModel;
private readonly CancellationTokenSource _cts = new CancellationTokenSource();
public MainWindow()
{
InitializeComponent();
_viewModel = new MainViewModel();
DataContext = _viewModel;
Loaded += async (s, e) => await _viewModel.Monitor.StartAsync(_cts.Token);
Closing += (s, e) =>
{
_cts.Cancel();
_viewModel.Monitor.Stop();
};
}
}
// MainWindow.xaml(部分)
/*
*/
说明
功能:
通过串口读取温度数据(模拟为字符串输入)。
实时更新WPF界面中的温度值和曲线(使用LiveCharts)。
将数据保存到SQLite数据库。
技术点:
MVVM模式:MainViewModel负责数据绑定和逻辑。
异步编程:StartAsync使用async/await读取串口。
泛型:DataBuffer
事件:OnTemperatureReceived通知UI更新。
数据库:SQLite存储数据,支持历史查询。
运行要求:
安装NuGet包:LiveCharts.Wpf、System.Data.SQLite。
确保串口可用,或使用虚拟串口工具(如VSPD)测试。
扩展建议:
添加报警功能(温度超阈值弹出提示)。
实现历史数据查询界面。
支持多种通信协议(通过工厂模式切换)。
总结
以上示例涵盖了C#高级编程的核心特性,并通过一个完整的上位机项目展示了实际应用。每个代码片段都包含:
详细注释:解释关键逻辑。
异常处理:确保鲁棒性。
上位机场景:结合实际开发需求。
下一步建议
实践:运行以上代码,修改参数或添加功能(如报警、导出数据)。
深入某部分:如果需要更详细的某模块(如设计模式或WPF数据绑定),请告诉我,我可以进一步展开。
项目扩展:尝试开发更复杂的上位机,如支持Modbus协议或多设备管理。
如果您有具体问题或需要某个部分的更深入代码(例如某个设计模式的实现或串口通信的调试技巧),请随时告知!