聚焦 C# 与工业数字化深度融合,本专栏由资深工业软件开发专家主理,系统分享 .NET 平台在智能制造、工业自动化及 MES/SCADA 系统开发中的实战经验。内容涵盖 OPC UA、Modbus、MQTT 等工业通信协议集成、实时数据采集与处理、数字孪生建模及边缘计算部署等核心技术,兼顾架构设计与代码落地。无论你是工控工程师还是 .NET 程序员,这里都有你需要的技术干货,助你在工业数字化浪潮中构建真正有价值的应用系统。
你有没有遇到过这种情况:工厂新上了一批设备型号,你的采集程序里写满了 今天这篇,就是教你用反射(一种让程序在运行时"自己认识"类型结构的技术)解决这个问题,新增设备类型不用再天天改源码。 上一节我们学了 LINQ to Objects,掌握了筛选、排序、分组统计数据的简洁写法。今天在这个基础上,我们进一步学习反射——让程序具备运行时动态识别和调用的能力。 反射到底是干什么的 反射(Reflection)说白了就是让程序在运行的时候,能"照镜子"看清楚一个类型有哪些属性、有哪些方法,甚至能动态创建对象、调用方法,而这些在写代码的时候根本不知道具体是什么类型。 这就好比你收到一个陌生的快递箱子,正常情况下你得提前知道箱子里是什么东西才能操作,反射就像给你一台"透视仪",箱子没打开你就能先看清楚里面装的是什么、有什么零件,然后针对性地操作。 为什么工业场景特别需要反射 工厂设备型号一直在增加,如果每种设备类型都写死在代码里,代码会越改越乱,反射能让你写一套"通用处理逻辑",新增设备类型时只需要新增一个类,不用改动原有的核心代码。 比如插件化的设备驱动加载、根据配置文件动态创建不同类型的报警处理器,这些场景反射都是核心技术支撑。 反射的核心操作 用反射根据名字动态创建对象 假设配置文件里写着设备类型是"BoilerDevice"这个字符串,你完全不需要在代码里写死 「一句话记住:反射的核心价值是"运行时才决定具体是什么类型",写代码的时候你完全不需要提前知道确切的类名。」 动态调用方法:不知道方法名也能调用 反射还能做到根据字符串名称动态调用一个方法,即使这个方法名是从配置文件或数据库里读出来的。 「重要提醒:反射的性能比直接调用代码要慢一些,因为多了一层"查找和解析"的过程,不适合放在每秒执行几万次的高频循环里,适合用在初始化、插件加载这类不那么频繁的场景。」 Step 1 新建控制台项目:文件 > 新建 > 项目 > 选择"控制台应用",命名 Step 2 反射相关类型在 Step 3 写反射代码时,对Copilot描述"根据字符串类名动态创建对象并调用方法",AI会生成对应的Type.GetType和Invoke代码骨架。 Step 4 写完反射代码后,选中代码右键 > Copilot > 检查异常处理,AI会提醒你反射操作找不到类型或方法时该怎么优雅处理,避免直接抛异常崩溃。 Vibe Coding Prompt 示例: 这段代码演示了工厂设备管理系统如何用反射根据配置字符串动态创建不同类型的设备对象,并统一调用它们的启动方法,新增设备类型完全不需要修改核心调度逻辑。 运行后,程序会依次尝试创建锅炉设备和冲压机设备,成功打印出对应设备的信息并调用启动方法,而对于配置里写错的"UnknownDevice"类型,程序会友好提示找不到类型,不会直接崩溃退出。 场景: 光伏电站的报警处理系统,需要根据报警级别配置动态调用对应的处理器类,方便后续新增报警类型不用改核心代码。 思路拆解: 运行后,普通报警和紧急报警会分别路由到对应的处理器类执行不同的处理逻辑,以后新增一个"严重"级别报警,只需要新增一个处理器类和一条映射记录,完全不用改动ProcessAlarm这段核心调度代码。 ❌ ❌ ❌ 在每秒执行几万次的高频循环里频繁使用反射调用方法 ✅ 反射操作放在初始化阶段一次性完成,高频循环里用普通方法调用 📌 原因:反射每次调用都要额外做类型查找解析,性能明显慢于直接调用,高频场景下会拖慢整体性能 学完本节,你掌握了:反射是什么、能解决什么"运行时才知道具体类型"的问题;怎么用Type.GetType和Activator.CreateInstance动态创建对象;还学会了报警处理器这类可扩展架构的设计思路。反射是插件化、可扩展架构的核心技术,学会了它,你的程序面对新增需求时会灵活得多。if (deviceType == "A型号") 这样的判断,每加一种新设备就要改代码、重新编译、重新发布,客户等得直跳脚。📌 上节回顾
💡 核心知识讲解
Type deviceType = typeof(BoilerDevice); // 获取类型信息
object instance = Activator.CreateInstance(deviceType); // 动态创建实例new BoilerDevice(),可以用反射根据这个字符串动态创建对象。string typeName = "BoilerDevice";
Type? type = Type.GetType(typeName);
object? device = type != null ? Activator.CreateInstance(type) : null;MethodInfo? method = type?.GetMethod("StartRunning");
method?.Invoke(device, null); // 动态调用StartRunning方法💻 VS2026 操作步骤
ReflectionDemo。System 和 System.Reflection 命名空间下,using System.Reflection; 即可使用。帮我写一段C#代码,用反射根据配置里的类名字符串动态创建设备对象实例,并调用这个对象的StartRunning方法,如果类型或方法不存在要给出友好提示而不是抛异常崩溃
📋 完整代码示例
using System;
using System.Reflection;
namespaceReflectionDemo
{
// 设备基类,定义通用行为
publicabstractclassDeviceBase
{
public abstract void StartRunning();
public abstract string GetDeviceInfo();
}
// 锅炉设备类
publicclassBoilerDevice : DeviceBase
{
publicdouble MaxTemperature { get; set; } = 200;
public override void StartRunning()
{
Console.WriteLine($"锅炉设备已启动,最高温度限制:{MaxTemperature}度");
}
public override string GetDeviceInfo()
{
return"锅炉设备 - 用于产生蒸汽";
}
}
// 冲压机设备类
publicclassStampingDevice : DeviceBase
{
publicint StrokesPerMinute { get; set; } = 60;
public override void StartRunning()
{
Console.WriteLine($"冲压机已启动,每分钟冲压次数:{StrokesPerMinute}");
}
public override string GetDeviceInfo()
{
return"冲压机设备 - 用于金属成型";
}
}
classProgram
{
static void Main()
{
// 模拟从配置文件读取的设备类型名称列表
string[] configuredDeviceTypeNames = newstring[]
{
"ReflectionDemo.BoilerDevice",
"ReflectionDemo.StampingDevice",
"ReflectionDemo.UnknownDevice"// 故意写一个不存在的类型,测试异常处理
};
foreach (string typeName in configuredDeviceTypeNames)
{
CreateAndStartDevice(typeName);
Console.WriteLine();
}
}
// 根据类型名称字符串,动态创建设备对象并启动
static void CreateAndStartDevice(string typeName)
{
Console.WriteLine($"正在尝试创建设备类型:{typeName}");
// 用反射根据字符串获取类型信息
Type? deviceType = Type.GetType(typeName);
if (deviceType == null)
{
Console.WriteLine($"未找到类型 {typeName},请检查配置是否正确");
return;
}
// 用反射动态创建对象实例
object? deviceInstance = Activator.CreateInstance(deviceType);
if (deviceInstance is DeviceBase device)
{
Console.WriteLine(device.GetDeviceInfo());
// 用反射获取StartRunning方法并调用
MethodInfo? startMethod = deviceType.GetMethod("StartRunning");
startMethod?.Invoke(deviceInstance, null);
}
else
{
Console.WriteLine("创建的对象不是有效的设备类型");
}
}
}
}🏭 工业实战小案例
using System;
using System.Collections.Generic;
namespaceSolarAlarmDemo
{
// 报警处理器基类
publicabstractclassAlarmHandlerBase
{
public abstract void HandleAlarm(string alarmMessage);
}
// 普通级别报警处理器
publicclassNormalAlarmHandler : AlarmHandlerBase
{
public override void HandleAlarm(string alarmMessage)
{
Console.WriteLine($"【普通报警】记录日志:{alarmMessage}");
}
}
// 紧急级别报警处理器
publicclassUrgentAlarmHandler : AlarmHandlerBase
{
public override void HandleAlarm(string alarmMessage)
{
Console.WriteLine($"【紧急报警】立即发送短信通知:{alarmMessage}");
}
}
classProgram
{
static void Main()
{
// 报警级别到处理器类名的映射,可以从配置文件读取
Dictionary<string, string> alarmLevelHandlerMap = new Dictionary<string, string>
{
{ "普通", "SolarAlarmDemo.NormalAlarmHandler" },
{ "紧急", "SolarAlarmDemo.UrgentAlarmHandler" }
};
ProcessAlarm("普通", "逆变器02电压轻微波动", alarmLevelHandlerMap);
ProcessAlarm("紧急", "逆变器05温度严重超标", alarmLevelHandlerMap);
}
static void ProcessAlarm(string alarmLevel, string alarmMessage, Dictionary<string, string> handlerMap)
{
if (!handlerMap.TryGetValue(alarmLevel, outstring? typeName)) return;
Type? handlerType = Type.GetType(typeName);
if (handlerType == null) return;
if (Activator.CreateInstance(handlerType) is AlarmHandlerBase handler)
{
handler.HandleAlarm(alarmMessage);
}
}
}
}⚠️ 这几个坑,我替你踩过了
Type.GetType("BoilerDevice") 只写类名不写完整命名空间 ✅ 写成 Type.GetType("ReflectionDemo.BoilerDevice") 完整命名空间加类名 📌 原因:GetType默认只在当前程序集里按完整名称查找,写不全直接返回null,找了半天以为反射不work其实是名字没写对Activator.CreateInstance(deviceType) 后直接强转类型使用 ✅ 用 is 模式匹配判断转换是否成功,比如 if (instance is DeviceBase device) 📌 原因:创建失败或类型不匹配时直接强转会抛异常,用is判断能优雅处理异常情况📝 本节总结