2026-05-09
C#
0

目录

📌 上节回顾
💡 核心知识讲解
一、算术运算符:数字怎么算
二、比较与逻辑运算符:条件怎么判
三、位运算符:寄存器里的硬核操作
四、赋值与复合运算符:简写更高效
💻 VS2026 操作步骤
🤖 Vibe Coding Prompt 示例
📋 完整代码示例
🏭 工业实战小案例
⚠️ 避坑提醒
这几个坑,我替你踩过了
📝 本节总结

车间里有台注塑机,PLC 每秒往上位机推一个 byte 类型的状态字。

你打开数据,看到的是 0b10110010——8个二进制位,每一位代表一个报警状态。

领导问:"哪几个报警同时触发了?"

你盯着这串数字,脑子里一片空白。

这不是 PLC 的事,也不是通信协议的事。

你缺的,是位运算。

今天这节课,把算术、逻辑、位运算三类运算符一次讲清楚,工控程序里的数据处理,你会顺多了。


📌 上节回顾

「上一节我们学了类型转换,掌握了隐式转换、显式转换和 Convert 类的使用方法。

今天在这个基础上,我们进一步学习运算符——数据有了,怎么算、怎么判断、怎么拆位,全靠它。」


💡 核心知识讲解

一、算术运算符:数字怎么算

算术运算符是最基础的一类,和数学里的加减乘除基本一样。

运算符含义示例结果
+加法3 + 58
-减法10 - 46
*乘法6 * 742
/除法10 / 33(整数除法)
%取余10 % 31

「注意:两个 int 相除,结果还是 int,小数部分直接丢掉,不是四舍五入。」

工业类比:生产线上统计每班产量时,% 取余运算非常常用。

比如每箱装 24 件,生产了 100 件,100 % 24 = 4,说明最后一箱还差 20 件才装满。


二、比较与逻辑运算符:条件怎么判

比较运算符(也叫关系运算符)用来判断两个值的大小关系,结果只有两种:truefalse

运算符含义示例
==等于temp == 85
!=不等于status != 0
> / <大于 / 小于count > 100
>= / <=大于等于 / 小于等于voltage <= 380

逻辑运算符用来组合多个条件,这在工控报警里极其常见。

运算符含义说明
&&与(AND)两个条件都成立才为 true
||或(OR)有一个成立就为 true
!非(NOT)取反

工业类比:把 && 想成 PLC 梯形图里的串联触点|| 想成并联触点! 就是常闭触点

⚠️ 注意区分 =(赋值)和 ==(比较)。if (temp = 85) 是赋值,不是判断,编译器会报错。这是新手最常犯的错之一。


三、位运算符:寄存器里的硬核操作

位运算(Bitwise Operation)是直接对二进制位进行操作的运算。

在工业开发里,PLC 状态字、Modbus 寄存器、设备 I/O 状态,全都是按位打包的数据。

不懂位运算,就读不懂这些数据。

常用位运算符一览:

运算符名称作用
&按位与提取指定位(掩码操作)
|按位或强制置位某一位
^按位异或翻转指定位
~按位取反所有位取反
<<左移相当于乘以 2 的 N 次方
>>右移相当于除以 2 的 N 次方

最常用的是 &(按位与)——用来提取某一位的状态。

工业类比:把一个 byte 想成一排 8 个开关,每个开关控制一个报警灯。

& 加上一个"掩码",就像拿一张只开了一个孔的纸盖上去,只看你想看的那一位。

状态字: 1011 0010 掩码: & 0000 0010 (只看第1位) 结果: 0000 0010 (第1位是1,说明该报警触发了)

「掩码(Mask):一个专门用来提取或屏蔽特定位的二进制数,工控程序里随处可见。」


四、赋值与复合运算符:简写更高效

C# 支持复合赋值运算符,写法更简洁,逻辑更清晰。

写法等价于
count += 1count = count + 1
total -= losstotal = total - loss
flags |= 0x04flags = flags | 0x04(置位第2位)
flags &= ~0x04flags = flags & ~0x04(清除第2位)

flags |= 0x04flags &= ~0x04 这两句是工控代码里置位清位的标准写法,记住它。


💻 VS2026 操作步骤

Step 1:新建控制台项目

打开 VS2026,选择 文件 > 新建 > 项目,搜索"控制台应用",选择 C# 控制台应用(.NET 10),项目名填 OperatorDemo,点击创建。

VS2026 Copilot 提示:创建完成后,Copilot 会自动识别到这是一个新项目,右侧 Copilot 面板会弹出"是否需要添加常用命名空间"的建议,直接点"接受"即可。

Step 2:打开 Program.cs,清空默认代码

VS2026 默认生成 Hello, World! 模板代码。全选删除,从第一行开始写你自己的逻辑。

Step 3:输入代码,使用 Copilot 自动补全

当你输入 // 提取设备状态字的第3位报警 这行注释后,停顿 1 秒,Copilot 会自动给出位运算的补全建议,按 Tab 接受即可。

Step 4:运行调试

F5 启动调试,在"输出"窗口查看打印结果。如果要单步调试,在关键行按 F9 打断点,再按 F5 运行,鼠标悬停变量名可以直接看当前值。


🤖 Vibe Coding Prompt 示例

如果你想让 Copilot 直接帮你生成位运算的工业场景代码,可以这样写 Prompt:

用 C# 14 写一个方法,输入参数是一个 byte 类型的设备状态字, 输出每一位对应的报警名称(共8个报警,名称我会在代码里定义), 使用位运算提取每一位,注释要详细,变量名用工业语义命名。

Copilot 会直接生成带注释的完整方法,你只需要把报警名称替换成实际设备的定义。

image.png


📋 完整代码示例

这段代码演示了三类运算符在工业场景中的典型用法:温度超限判断、产量统计取余、以及设备状态字的位提取。

csharp
// OperatorDemo - 运算符工业场景综合演示 // 运行环境:VS2026 + .NET 10 + C# 14 using System; class OperatorDemo { static void Main() { // ===== 1. 算术运算符:产量统计 ===== int totalProduction = 1583; // 总产量(件) int boxCapacity = 24; // 每箱容量(件) // 计算完整箱数和散件数量 int fullBoxCount = totalProduction / boxCapacity; int remainingParts = totalProduction % boxCapacity; Console.WriteLine($"总产量:{totalProduction} 件"); Console.WriteLine($"完整箱数:{fullBoxCount} 箱"); Console.WriteLine($"散件数量:{remainingParts} 件"); Console.WriteLine(); // ===== 2. 比较与逻辑运算符:温度报警判断 ===== double deviceTemp = 92.5; // 当前设备温度(°C) double alarmThreshold = 90.0; // 报警阈值(°C) double criticalThreshold = 100.0; // 紧急停机阈值(°C) bool isCoolingSystemOk = true; // 冷却系统是否正常 // 判断是否需要触发报警 bool isOverheat = deviceTemp > alarmThreshold; // 判断是否需要紧急停机:温度超临界值 且 冷却系统故障 bool needEmergencyStop = deviceTemp >= criticalThreshold && !isCoolingSystemOk; Console.WriteLine($"当前温度:{deviceTemp}°C"); Console.WriteLine($"超温报警:{isOverheat}"); Console.WriteLine($"紧急停机:{needEmergencyStop}"); Console.WriteLine(); // ===== 3. 位运算符:解析设备状态字 ===== // 模拟从 Modbus 寄存器读取的设备状态字(1 byte) // 每一位含义(从低位到高位): // Bit0 = 电机过载 Bit1 = 温度超限 // Bit2 = 压力异常 Bit3 = 急停触发 // Bit4~7 = 保留位 byte deviceStatusByte = 0b00001011; // 二进制:00001011 // 定义各报警位的掩码 const byte MASK_MOTOR_OVERLOAD = 0b00000001; // Bit0 const byte MASK_TEMP_ALARM = 0b00000010; // Bit1 const byte MASK_PRESSURE_ALARM = 0b00000100; // Bit2 const byte MASK_EMERGENCY_STOP = 0b00001000; // Bit3 // 用按位与提取每一位的状态 bool motorOverload = (deviceStatusByte & MASK_MOTOR_OVERLOAD) != 0; bool tempAlarm = (deviceStatusByte & MASK_TEMP_ALARM) != 0; bool pressureAlarm = (deviceStatusByte & MASK_PRESSURE_ALARM) != 0; bool emergencyStop = (deviceStatusByte & MASK_EMERGENCY_STOP) != 0; Console.WriteLine($"状态字原始值:0x{deviceStatusByte:X2}(二进制:{Convert.ToString(deviceStatusByte, 2).PadLeft(8, '0')})"); Console.WriteLine($"电机过载:{motorOverload}"); Console.WriteLine($"温度超限:{tempAlarm}"); Console.WriteLine($"压力异常:{pressureAlarm}"); Console.WriteLine($"急停触发:{emergencyStop}"); Console.WriteLine(); // ===== 4. 复合赋值运算符:动态置位与清位 ===== byte controlFlags = 0b00000000; // 置位第2位(压力异常报警) controlFlags |= MASK_PRESSURE_ALARM; Console.WriteLine($"置位后:{Convert.ToString(controlFlags, 2).PadLeft(8, '0')}"); // 清除第2位(报警复位) controlFlags &= unchecked((byte)~MASK_PRESSURE_ALARM); Console.WriteLine($"清位后:{Convert.ToString(controlFlags, 2).PadLeft(8, '0')}"); } }

image.png

运行后,控制台会依次打印产量统计结果、温度报警状态、设备状态字的每一位解析结果,以及置位/清位前后的二进制变化。

你可以把 deviceStatusByte 的值改成任意数字,观察哪些报警位被触发,这就是真实上位机解析 PLC 数据的核心逻辑。


🏭 工业实战小案例

场景: 冲压车间上位机每 500ms 读取一次压力机的状态字(ushort 类型,16位),需要判断第 4 位(模具到位信号)和第 7 位(冲压完成信号)是否同时为 1,才允许触发下一次冲压动作。

思路拆解:

  1. 定义两个掩码,分别对应第 4 位和第 7 位
  2. & 提取这两位的状态
  3. && 判断两个条件是否同时成立
  4. 结果为 true 时,触发下一次冲压指令
csharp
// 冲压机状态字解析 - 判断是否允许触发下一次冲压 ushort stampingStatusWord = 0b0000000010010000; // 模拟读取的状态字 // 掩码定义(ushort 16位) const ushort MASK_DIE_IN_PLACE = 1 << 4; // 第4位:模具到位 const ushort MASK_STAMP_COMPLETE = 1 << 7; // 第7位:冲压完成 // 提取两个信号位 bool dieInPlace = (stampingStatusWord & MASK_DIE_IN_PLACE) != 0; bool stampComplete = (stampingStatusWord & MASK_STAMP_COMPLETE) != 0; // 两个条件同时满足,才允许触发下一次冲压 bool allowNextStamp = dieInPlace && stampComplete; Console.WriteLine($"模具到位:{dieInPlace},冲压完成:{stampComplete}"); Console.WriteLine($"允许触发下一次冲压:{allowNextStamp}");

image.png

运行后输出 允许触发下一次冲压:True,说明两个信号均有效,上位机可以发送下一次冲压指令。把 stampingStatusWord 改成其他值,你会看到条件不满足时输出 False,这就是真实冲压机联锁逻辑的软件侧实现。


⚠️ 避坑提醒

这几个坑,我替你踩过了

坑1:整数除法丢精度

double ratio = 7 / 2;(结果是 3.0,不是 3.5)

double ratio = 7.0 / 2;(double)7 / 2;

📌 原因:两个 int 相除,C# 直接做整数除法,小数部分截断,不会自动转成 double


坑2:&&& 混用

if (deviceTemp > 90 & isCoolingSystemOk) (单 & 是位运算,用在 bool 上虽然能跑,但语义不对,且不会短路求值)

if (deviceTemp > 90 && isCoolingSystemOk) (双 && 才是逻辑与,有短路特性)

📌 原因:&&短路求值——左边为 false 时右边直接跳过,避免不必要的运算甚至空引用异常。


坑3:位运算结果类型提升

byte result = deviceStatusByte & MASK_TEMP_ALARM;(编译报错)

byte result = (byte)(deviceStatusByte & MASK_TEMP_ALARM);

📌 原因:C# 对 byte 做位运算时,会自动提升为 int 类型,结果也是 int,赋值回 byte 必须显式强转。


📝 本节总结

「学完本节,你掌握了:」

算术运算符的整数除法陷阱和取余在产量统计中的实用技巧;比较运算符与逻辑运算符的组合用法,能写出多条件的设备报警判断;位运算的核心原理,可以用掩码从 PLC 状态字里精准提取任意一位的信号;复合赋值运算符的置位与清位写法,这是工控代码里的标准套路。

运算符这关过了,后面写数据处理逻辑会顺很多。


📖 本文是《C# 工业数字化应用开发专家》系列第 012 节

上一节:【类型转换:隐式、显式与 Convert 类】

下一节:【字符串操作:拼接、格式化、插值表达式】(明天更新)

💬 你在工作中遇到过解析设备状态字的需求吗?

欢迎在评论区说说你们设备的状态字是怎么定义的,也许下一篇案例就来自你的留言。

🔔 还没关注的同学记得点击关注,系列课程持续更新,学完这 420 节,从工厂小白到工业软件开发专家,我们一起走。

#C#编程 #工业数字化 #工控软件开发 #编程入门 #制造业数字化转型

本文作者:技术老小子

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!