
003011025_.NET 枚举值完整使用指南摘要本文面向工业上位机开发系统梳理 .NET 枚举Enum从定义、使用到与 PLC/MES 数据交互的完整流程重点介绍整数、枚举、字符串之间的安全转换以及[Description]中文描述、[Flags]组合状态等特性并通过设备状态上报、配方变更上报、状态机三个实战场景替代硬编码最后总结常见坑与最佳实践。一、枚举是什么为什么工业项目必须用枚举Enum是一种强类型的常量集合用来表示一组固定的、相关的离散值。工业场景不用枚举的致命问题csharp:// ❌ 硬编码魔法数字字符串数组极易出错 Liststring list new Liststring() { RUN, DOWN, IDLE, PM }; var eqpStateMessage list[eqpStatNo - 1].ToString();索引越界风险如果 PLC 返回 5会直接抛出IndexOutOfRangeException导致程序崩溃可读性差看到数字 1 不知道代表什么必须看注释无编译时检查传错数字编译器不会报错只会在运行时出问题维护困难新增或修改状态时需要修改所有用到的地方用枚举的优势csharp:// ✅ 强类型枚举安全可靠 public enum EqpMainStatus { RUN 1, DOWN 2, Idle 3, PM 4 }类型安全编译器会检查类型不能传任意数字可读性高EqpMainStatus.RUN比数字 1 清晰 100 倍编译时检查传错值会直接编译报错易于维护修改枚举值只需要改定义处功能强大支持转换、特性、扩展方法等高级用法二、第一步定义枚举基本语法csharp:// 访问修饰符 enum 枚举名 { 枚举值 整数, ... } public enum EqpMainStatus { RUN 1, // 运行中 DOWN 2, // 停机 Idle 3, // 空闲 PM 4 // 维护中 }工业项目定义规范枚举名用名词EqpMainStatus、RecipeEventType、AlarmLevel枚举值用帕斯卡命名法RUN、DOWN、Enable显式指定整数值必须和 PLC 或 MES 约定的值完全一致不要用 0 作为有效业务值因为枚举的默认值是 0避免混淆 “未初始化” 和 “有效状态”添加 XML 注释说明每个枚举值的含义工业标准示例csharp:/// summary /// 配方事件类型与MES接口约定 /// /summary public enum RecipeEventType { /// summary /// 启用配方 /// /summary Enable 1, /// summary /// 创建配方 /// /summary Create 2, /// summary /// 修改配方 /// /summary Modify 3 } /// summary /// 报警级别 /// /summary public enum AlarmLevel { /// summary /// 提示 /// /summary Info 1, /// summary /// 警告 /// /summary Warn 2, /// summary /// 错误 /// /summary Error 3, /// summary /// 致命 /// /summary Fatal 4 }三、第二步枚举的基本使用1. 声明枚举变量csharp:// 声明并赋值 EqpMainStatus currentStatus EqpMainStatus.RUN; // 默认值是0如果没有定义0的枚举值会显示数字0 EqpMainStatus defaultStatus default; // 输出02. 枚举比较csharp:if (currentStatus EqpMainStatus.RUN) { // 设备运行中 } else if (currentStatus EqpMainStatus.DOWN) { // 设备停机 }3. 转换为字符串csharp:string statusStr currentStatus.ToString(); // 输出RUN这正是你 MES 上报需要的接口要求的RUN、DOWN字符串直接用ToString()就能得到完全不需要硬编码字符串数组。四、第三步核心类型转换工业场景最常用工业项目中 90% 的枚举使用都是PLC 数字→枚举→MES 字符串的转换流程这是必须掌握的核心。1. 整数 → 枚举PLC 读取后转换PLC 返回的都是整数需要转换成枚举类型。显式转换简单但不安全csharp:int plcValue 1; EqpMainStatus status (EqpMainStatus)plcValue; // 输出EqpMainStatus.RUN问题如果 PLC 返回无效值如 5转换不会报错只会显示数字 5导致后续逻辑异常。安全转换工业项目必须用使用Enum.IsDefined检查转换后的值是否有效csharp:int plcValue Device.PLC.GetEqpStateMassage; if (Enum.IsDefined(typeof(EqpMainStatus), plcValue)) { EqpMainStatus status (EqpMainStatus)plcValue; // 正常处理 } else { // 无效值处理记录日志使用默认值 Logger.LogCommucation.Error($PLC返回无效的设备状态值{plcValue}); EqpMainStatus status EqpMainStatus.Idle; }2. 枚举 → 整数写入 PLC 时转换csharp:EqpMainStatus status EqpMainStatus.RUN; int plcValue (int)status; // 输出1 Device.PLC.WriteInt(DB1.DBW0, plcValue);3. 字符串 → 枚举解析配置或 MES 返回csharp:string mesStatus DOWN; // 方法1Enum.Parse可能抛出异常 EqpMainStatus status1 (EqpMainStatus)Enum.Parse(typeof(EqpMainStatus), mesStatus); // 方法2Enum.TryParse推荐安全不抛异常 if (Enum.TryParseEqpMainStatus(mesStatus, ignoreCase: true, out var status2)) { // 解析成功 } else { // 解析失败处理 Logger.LogCommucation.Error($无效的设备状态字符串{mesStatus}); }ignoreCase: true表示忽略大小写run、Run、RUN都能正确解析。五、第四步枚举特性工业场景高级用法1. [Description] 特性中文描述给枚举值添加中文描述用于 UI 显示和日志记录操作人员不需要看英文。第一步引用命名空间csharp:using System.ComponentModel;第二步给枚举添加特性csharp:public enum EqpMainStatus { [Description(运行中)] RUN 1, [Description(停机)] DOWN 2, [Description(空闲)] Idle 3, [Description(维护中)] PM 4 }第三步编写扩展方法获取描述csharp:using System.Reflection; public static class EnumExtensions { /// summary /// 获取枚举的Description特性值 /// /summary public static string GetDescription(this Enum enumValue) { FieldInfo field enumValue.GetType().GetField(enumValue.ToString()); DescriptionAttribute attribute field.GetCustomAttributeDescriptionAttribute(); return attribute?.Description ?? enumValue.ToString(); } }第四步使用csharp:EqpMainStatus status EqpMainStatus.RUN; string chineseDesc status.GetDescription(); // 输出运行中 // UI显示中文 StatusTextBlock.Text $设备状态{chineseDesc}; // 日志记录中文 Logger.LogCommucation.Info($设备状态变更为{chineseDesc});2. [Flags] 特性位枚举组合状态当一个值可以表示多个状态的组合时使用比如多个报警同时存在。定义位枚举csharp:[Flags] public enum AlarmType { None 0, CameraError 1 0, // 1 PlcError 1 1, // 2 LightError 1 2, // 4 MotorError 1 3 // 8 }使用组合状态csharp:// 同时存在相机和PLC报警 AlarmType alarms AlarmType.CameraError | AlarmType.PlcError; // 检查是否包含某个报警 if (alarms.HasFlag(AlarmType.CameraError)) { // 有相机报警 } // 移除一个报警 alarms ~AlarmType.CameraError;六、工业场景实战用法直接替换你的代码实战 1设备状态上报替换你之前的硬编码代码原代码有问题csharp:// ❌ 硬编码字符串数组索引越界风险 Liststring list new Liststring() { RUN, DOWN, IDLE, PM }; var eqpStateMessage list[eqpStatNo - 1].ToString(); requestObj[MAINSTATUS] eqpStateMessage;优化后代码安全可靠csharp:// ✅ 安全转换PLC值为枚举 int eqpStatNo Device.PLC.GetEqpStateMassage; if (!Enum.IsDefined(typeof(EqpMainStatus), eqpStatNo)) { Logger.LogCommucation.Error($PLC返回无效设备状态{eqpStatNo}使用默认值Idle); eqpStatNo (int)EqpMainStatus.Idle; } EqpMainStatus currentStatus (EqpMainStatus)eqpStatNo; // 直接ToString()得到MES需要的字符串 requestObj[MAINSTATUS] currentStatus.ToString(); // UI显示中文 UpdateProcess($设备状态{currentStatus.GetDescription()});实战 2配方变更上报原代码硬编码数字csharp:// ❌ 硬编码数字1不知道代表什么 requestObj[RECIPE_EVENT] 1;优化后代码清晰易懂csharp:// ✅ 用枚举代替硬编码一目了然 RecipeEventType eventType RecipeEventType.Enable; requestObj[RECIPE_EVENT] ((int)eventType).ToString(); // 日志记录 Logger.LogCommucation.Info($上报配方{eventType}事件);实战 3设备状态机用枚举管理设备状态机比用 int 清晰太多csharp:public class DeviceStateMachine { private EqpMainStatus _currentState EqpMainStatus.Idle; public void TransitionTo(EqpMainStatus newState) { // 检查状态转换是否合法 if (!IsValidTransition(_currentState, newState)) { throw new InvalidOperationException($无法从{_currentState}转换到{newState}); } _currentState newState; // 上报状态变化到MES _statusReportManager.ReportStatusChange(newState); } private bool IsValidTransition(EqpMainStatus from, EqpMainStatus to) { return (from, to) switch { (EqpMainStatus.Idle, EqpMainStatus.RUN) true, (EqpMainStatus.RUN, EqpMainStatus.DOWN) true, (EqpMainStatus.DOWN, EqpMainStatus.Idle) true, _ false }; } }七、常见坑与最佳实践1. 不要用 0 作为有效业务值csharp:// ❌ 错误0是枚举默认值容易混淆未初始化和有效状态 public enum EqpMainStatus { RUN 0, DOWN 1 } // ✅ 正确0表示未定义有效状态从1开始 public enum EqpMainStatus { Undefined 0, RUN 1, DOWN 2 }2. 总是检查枚举值的有效性永远不要相信外部输入PLC、MES、用户输入必须用Enum.IsDefined检查csharp:// ❌ 错误直接转换无效值会导致后续逻辑异常 EqpMainStatus status (EqpMainStatus)plcValue; // ✅ 正确先检查再转换 if (Enum.IsDefined(typeof(EqpMainStatus), plcValue)) { EqpMainStatus status (EqpMainStatus)plcValue; } else { // 处理无效值 }3. 不要随意修改枚举的整数值一旦枚举值已经持久化到数据库、配置文件或 MES 系统就不要修改它的整数值否则会导致历史数据解析错误。4. 枚举值不要太多如果枚举值超过 10 个考虑是否需要拆分成多个枚举或者用其他方式管理。5. 不要用枚举存储动态数据枚举是编译时常量不能在运行时添加或修改。如果数据是动态的比如配方列表不要用枚举。八、总结工业项目枚举使用流程定义根据 PLC 和 MES 的约定定义强类型枚举显式指定整数值添加注释转换从 PLC 读取整数后用Enum.IsDefined安全转换为枚举使用在代码中用枚举值进行比较和逻辑判断可读性高上报用ToString()转换为 MES 需要的字符串用(int)转换为需要的数字显示用[Description]特性和扩展方法获取中文描述用于 UI 和日志