C# 语言基础(变量、数据类型、流程控制、面向对象编程)

讲解目标:针对工控上位机开发场景,剥离无用复杂语法,聚焦「变量、数据类型、流程控制、面向对象」的核心实用知识点,配合工控实战案例,让你快速掌握并落地使用。

一、 变量与数据类型(工控数据存储核心)

1. 变量基础(工控场景必备操作)

变量是用于存储工控场景中动态数据的容器(如测量值、设备状态、通信参数等),核心遵循「先定义 / 声明,后使用」的原则。

(1) 变量声明语法
cs 复制代码
// 语法:数据类型 变量名;
int defectCount; // 声明:存储缺陷数量的变量
double outerDiameter; // 声明:存储轴承外径的变量

// 语法:数据类型 变量名 = 初始值;(推荐:工控场景优先初始化,避免空值异常)
int port = 502; // 声明并初始化:Modbus通信端口
string cameraIP = "192.168.1.100"; // 声明并初始化:相机IP地址
bool isCameraConnected = false; // 声明并初始化:相机连接状态
(2) 变量命名规范(工控项目可维护性关键)
  • 采用「驼峰命名法」:首字母小写,后续单词首字母大写(如defectCountinnerDiameter)。
  • 见名知意:避免abtemp等无意义命名,直接体现变量用途(如detectTime= 检测时间,ocvResult= 字符识别结果)。
  • 避免关键字:不能使用intstringif等 C# 关键字作为变量名。

2. 核心数据类型(工控场景高频使用)

按用途分为「值类型」和「引用类型」,以下仅罗列工控上位机开发中必须掌握的类型,无实用价值的类型不做赘述。

(1) 值类型(直接存储数据,常用工控场景)
数据类型 大小 核心用途 工控实战示例
int 4 字节 存储整数型参数(计数、端口号、数量、编号等) 缺陷数量:int defectCount = 0;串口编号:int comPort = 3;零件编号:int partNo = 1001;
double 8 字节 存储浮点型参数(尺寸、坐标、精度值、温度等) 轴承内径:double innerDiameter = 19.95;定位 X 坐标:double xPos = 256.32;温度值:double temp = 25.8;
bool 1 字节 存储状态信息(是 / 否、合格 / 不合格、连接 / 断开等) 检测结果:bool isPass = true;TCP 连接状态:bool isTcpConnected = false;超差状态:bool isOverTolerance = false;
DateTime 8 字节 存储时间信息(检测时间、日志时间、设备启动时间等) 当前检测时间:DateTime detectTime = DateTime.Now;起始查询时间:DateTime startTime = new DateTime(2026,1,1);
(2) 引用类型(存储数据地址,常用工控场景)
数据类型 核心用途 工控实战示例
string 存储文本信息(IP 地址、识别结果、日志内容、设备型号等) 相机 IP:string cameraIP = "192.168.1.100";OCV 识别结果:string ocvResult = "A2B3C4";设备型号:string deviceModel = "Basler acA2040";
(3) 类型转换(工控场景高频操作)

工控中常需在不同类型间转换(如文本框输入的stringint/double、测量值doublestring显示),核心掌握 2 种转换方式:

(2) while 循环(适用于未知循环次数,满足条件即执行)

3. 异常处理语句(try-catch-finally,工控程序稳定性保障)

工控场景中存在大量不可控异常(如相机断开、数据库连接失败、串口未打开),异常处理可避免程序崩溃,是现场部署的必备技能。

  1. 显式转换(强制转换 / 专用方法,推荐工控场景使用)

    cs 复制代码
    // 场景1:TextBox输入的端口号(string)转int
    string portStr = txtPort.Text;
    int port = int.Parse(portStr); // 字符串转int
    // 场景2:测量值double转string显示在TextBox
    double outerDiam = 49.98;
    txtOuterDiam.Text = outerDiam.ToString("F2"); // 保留2位小数转换
    // 场景3:字符串转double(尺寸参数输入)
    string diamStr = txtInnerDiam.Text;
    double innerDiam = double.Parse(diamStr);
    1. 安全转换(避免转换失败导致程序崩溃,推荐异常场景使用)
    cs 复制代码
    string portStr = txtPort.Text;
    int port;
    // TryParse:转换成功返回true,失败返回false,不抛异常
    if (int.TryParse(portStr, out port))
    {
        // 转换成功,执行后续逻辑
        InitSerialPort(port);
    }
    else
    {
        // 转换失败,提示用户
        MessageBox.Show("请输入有效的端口号", "错误提示");
    }

    二、 流程控制语句(工控业务逻辑编排核心)

    流程控制用于实现工控业务逻辑(如超差判断、批量检测、异常分支处理),核心掌握 3 类语句,无需深入复杂语法。

    1. 条件判断语句(根据状态 / 数据执行不同逻辑)

    (1) if-else 语句(工控首选,适用于大部分条件场景)
  2. 语法结构:

    cs 复制代码
    // 单条件
    if (条件表达式)
    {
        // 条件为true时执行
    }
    // 双条件
    if (条件表达式)
    {
        // 条件为true时执行
    }
    else
    {
        // 条件为false时执行
    }
    // 多条件
    if (条件1)
    {
        // 条件1为true时执行
    }
    else if (条件2)
    {
        // 条件1为false、条件2为true时执行
    }
    else
    {
        // 所有条件都为false时执行
    }

    工控实战示例(轴承尺寸超差判断,核心业务逻辑):

    cs 复制代码
    // 已知:轴承外径公差49.8mm ~ 50.2mm,内径公差19.8mm ~ 20.2mm
    double outerDiam = 50.3; // 测量得到的外径
    double innerDiam = 19.7; // 测量得到的内径
    bool isPass = true; // 初始化合格状态
    string errorMsg = ""; // 异常信息
    
    // 外径判断
    if (outerDiam < 49.8 || outerDiam > 50.2)
    {
        isPass = false;
        errorMsg += "外径超差;";
    }
    // 内径判断
    if (innerDiam < 19.8 || innerDiam > 20.2)
    {
        isPass = false;
        errorMsg += "内径超差;";
    }
    // 最终结果判断
    if (isPass)
    {
        lblResult.Text = "检测合格";
        lblResult.ForeColor = Color.Green;
    }
    else
    {
        lblResult.Text = "检测不合格:" + errorMsg;
        lblResult.ForeColor = Color.Red;
        System.Media.SystemSounds.Exclamation.Play(); // 超差报警
    }
    (2) switch 语句(适用于多分支固定值判断)
  3. 语法结构:

    cs 复制代码
    switch (判断变量)
    {
        case 值1:
            // 变量等于值1时执行
            break; // 跳出switch,避免穿透
        case 值2:
            // 变量等于值2时执行
            break;
        default:
            // 变量不等于所有case值时执行
            break;
    }

    工控实战示例(设备状态判断):

    cs 复制代码
    // 设备状态码:1=待机,2=检测中,3=故障,4=停机
    int deviceStatus = 3;
    string statusDesc = "";
    
    switch (deviceStatus)
    {
        case 1:
            statusDesc = "设备待机";
            lblStatus.ForeColor = Color.Blue;
            break;
        case 2:
            statusDesc = "检测中";
            lblStatus.ForeColor = Color.Green;
            break;
        case 3:
            statusDesc = "设备故障";
            lblStatus.ForeColor = Color.Red;
            System.Media.SystemSounds.Beep.Play();
            break;
        case 4:
            statusDesc = "设备停机";
            lblStatus.ForeColor = Color.Gray;
            break;
        default:
            statusDesc = "未知状态";
            break;
    }
    lblStatus.Text = statusDesc;

    2. 循环语句(批量处理 / 重复执行逻辑)

    (1) for 循环(适用于已知循环次数的场景)
  4. 语法结构:

    cs 复制代码
    // 初始化循环变量 → 循环条件 → 循环变量自增/自减
    for (int i = 0; i < 循环次数; i++)
    {
        // 循环执行的逻辑
    }
  5. 工控实战示例(批量读取 10 个零件测量数据):

    cs 复制代码
    // 循环读取10个零件的外径数据并打印
    for (int i = 0; i < 10; i++)
    {
        // 模拟获取第i+1个零件的测量值
        double measureDiam = GetOuterDiameter(i + 1);
        // 打印日志
        rtbLog.AppendText($"第{i+1}个零件外径:{measureDiam:F2}mm\r\n");
        // 批量存储到数据库
        InsertMeasureData(i + 1, measureDiam, DateTime.Now);
    }
    // 自定义方法:模拟获取测量值
    private double GetOuterDiameter(int partNo)
    {
        // 实际场景中替换为真实视觉测量逻辑
        Random random = new Random();
        return 49.8 + random.NextDouble() * 0.4; // 生成49.8~50.2之间的随机数
    }
  6. 语法结构:

    cs 复制代码
    // 先判断条件,条件为true则执行循环体
    while (条件表达式)
    {
        // 循环执行的逻辑
        // 必须有修改条件的语句,避免死循环
    }
  7. 工控实战示例(等待相机连接成功):

    cs 复制代码
    bool isCameraConnected = false;
    int retryCount = 0; // 重试次数
    const int maxRetry = 5; // 最大重试次数
    
    // 循环等待相机连接,最多重试5次
    while (!isCameraConnected && retryCount < maxRetry)
    {
        try
        {
            CameraHelper camera = new CameraHelper("192.168.1.100");
            camera.InitCamera();
            isCameraConnected = true; // 连接成功,修改条件
            rtbLog.AppendText("相机连接成功\r\n");
        }
        catch (Exception ex)
        {
            retryCount++; // 重试次数自增
            rtbLog.AppendText($"第{retryCount}次连接失败:{ex.Message}\r\n");
            System.Threading.Thread.Sleep(1000); // 等待1秒后重试
        }
    }
    
    if (!isCameraConnected)
    {
        MessageBox.Show($"相机连接失败,已重试{maxRetry}次", "错误提示");
    }
  8. 语法结构:

    cs 复制代码
    try
    {
        // 可能抛出异常的代码(相机操作、通信、数据库操作等)
    }
    catch (Exception ex)
    {
        // 捕获异常并处理(提示用户、记录日志等)
    }
    finally
    {
        // 无论是否发生异常,都会执行的代码(释放资源、关闭连接等)
    }
  9. 工控实战示例(串口操作异常处理):

    cs 复制代码
    private SerialPort _serialPort;
    
    private void OpenSerialPort()
    {
        try
        {
            _serialPort = new SerialPort();
            _serialPort.PortName = "COM3";
            _serialPort.BaudRate = 9600;
            _serialPort.Open(); // 可能抛出异常:串口被占用、不存在等
            lblSerialStatus.Text = "串口已打开";
            lblSerialStatus.ForeColor = Color.Green;
        }
        catch (Exception ex)
        {
            // 捕获异常并提示用户
            MessageBox.Show($"串口打开失败:{ex.Message}", "错误提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
            // 记录异常日志
            rtbLog.AppendText($"【错误】{DateTime.Now}:串口打开失败-{ex.Message}\r\n");
        }
        finally
        {
            // 无需额外操作,若串口打开失败,_serialPort为null,无需释放
            // 若为相机/数据库连接,此处可执行释放操作
        }
    }

三、 面向对象编程(工控代码封装与复用核心)

工控上位机开发强调代码的可维护性和复用性(如多个项目共用相机操作、数据库操作逻辑),面向对象编程(OOP)是实现该目标的核心手段,重点掌握「类」和「方法」的封装,无需深入继承、多态等复杂特性。

1. 核心概念:类(Class)

类是「相关数据和功能的封装容器」,工控场景中可按「功能模块」划分类(如相机操作类、数据库操作类、视觉检测类),实现代码隔离和复用。

  • 类的定义语法:

    cs 复制代码
    // 访问修饰符:public(公开,其他类可访问)/ private(私有,仅本类内部访问)
    public class 类名
    {
        // 1. 私有字段(存储类的内部数据,外部不可直接访问)
        private 数据类型 字段名;
        // 2. 构造函数(初始化类的实例,创建对象时自动执行)
        public 类名(参数列表)
        {
            // 初始化字段
        }
        // 3. 公共方法(提供外部访问的功能,实现核心逻辑)
        public 返回值类型 方法名(参数列表)
        {
            // 方法逻辑
        }
        // 4. 私有方法(本类内部使用,辅助实现公共方法逻辑)
        private 返回值类型 方法名(参数列表)
        {
            // 方法逻辑
        }
    }

2. 核心概念:方法(Method)

方法是「封装单一功能的代码块」,一个方法对应一个核心功能(如「初始化相机」、「插入检测数据」、「发送 Modbus 指令」),便于调试和修改。

  • 方法的定义语法:

    cs 复制代码
    // 访问修饰符 返回值类型 方法名(参数列表)
    public double GetOuterDiameter(int partNo)
    {
        // 方法逻辑
        Random random = new Random();
        double diam = 49.8 + random.NextDouble() * 0.4;
        return diam; // 返回测量值(与返回值类型一致)
    }
    // 无返回值方法:返回值类型为void
    public void InsertMeasureData(int partNo, double diam, DateTime detectTime)
    {
        // 方法逻辑(存储数据,无需返回值)
        using (SqlConnection conn = new SqlConnection(_connStr))
        {
            string sql = "INSERT INTO T_Measure(PartNo, OuterDiam, DetectTime) VALUES(@PartNo, @OuterDiam, @DetectTime)";
            SqlCommand cmd = new SqlCommand(sql, conn);
            cmd.Parameters.AddWithValue("@PartNo", partNo);
            cmd.Parameters.AddWithValue("@OuterDiam", diam);
            cmd.Parameters.AddWithValue("@DetectTime", detectTime);
            conn.Open();
            cmd.ExecuteNonQuery();
        }
    }

3. 工控实战:类与方法的封装(相机操作类)

这是工控项目中最典型的封装示例,可直接复用在所有视觉检测项目中。

cs 复制代码
using System;
using HalconDotNet;
using Cognex.VisionPro;

// 相机操作类:封装相机初始化、图像采集、资源释放功能
public class CameraHelper : IDisposable
{
    // 私有字段:仅本类内部访问,存储相机实例和IP
    private HCamera _hCamera; // 相机实例
    private string _cameraIP; // 相机IP地址
    private bool _isDisposed = false; // 释放状态标记

    // 事件:图像采集完成后触发,用于传递采集到的图像
    public event Action<CogImage8Grey> ImageGrabbed;

    // 构造函数1:无参构造(默认IP)
    public CameraHelper()
    {
        _cameraIP = "192.168.1.100"; // 默认相机IP
    }

    // 构造函数2:带参构造(自定义IP,更灵活)
    public CameraHelper(string cameraIP)
    {
        _cameraIP = cameraIP; // 初始化相机IP
    }

    // 公共方法:初始化相机(外部可调用)
    public void InitCamera()
    {
        try
        {
            _hCamera = new HCamera();
            _hCamera.Open(_cameraIP); // 打开相机
            _hCamera.SetContinuousGrab(true); // 设置连续采集
            _hCamera.GrabStarted += HC_GrabStarted; // 绑定采集事件
            Console.WriteLine("相机初始化成功");
        }
        catch (Exception ex)
        {
            throw new Exception($"相机初始化失败:{ex.Message}"); // 抛出异常,由调用方处理
        }
    }

    // 私有方法:相机采集回调(仅本类内部使用,外部不可访问)
    private void HC_GrabStarted(object sender, EventArgs e)
    {
        HImage hImage = _hCamera.GrabImage(); // 采集Halcon图像
        CogImage8Grey cogImage = ConvertHImageToCogImage(hImage); // 转换为VisionPro图像
        ImageGrabbed?.Invoke(cogImage); // 触发事件,传递图像
    }

    // 私有方法:图像格式转换(辅助功能,内部封装)
    private CogImage8Grey ConvertHImageToCogImage(HImage hImage)
    {
        hImage.GetImagePointer1(out IntPtr ptr, out int type, out int width, out int height);
        return new CogImage8Grey(width, height, ptr);
    }

    // 公共方法:释放相机资源(外部可调用)
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    // 受保护方法:实际释放资源逻辑
    protected virtual void Dispose(bool disposing)
    {
        if (!_isDisposed)
        {
            if (disposing)
            {
                // 释放托管资源
                _hCamera?.Close(); // 关闭相机
                _hCamera?.Dispose(); // 释放相机实例
            }
            _isDisposed = true;
        }
    }
}

4. 类的使用(实例化对象)

封装好的类需要「实例化对象」才能使用其功能,这是工控代码调用的核心步骤。

cs 复制代码
// 1. 实例化相机对象(使用带参构造,传入自定义IP)
CameraHelper cameraHelper = new CameraHelper("192.168.1.101");

try
{
    // 2. 调用对象的公共方法:初始化相机
    cameraHelper.InitCamera();

    // 3. 绑定对象的事件:接收采集到的图像
    cameraHelper.ImageGrabbed += CameraHelper_ImageGrabbed;

    lblCameraStatus.Text = "相机已连接";
    lblCameraStatus.ForeColor = Color.Green;
}
catch (Exception ex)
{
    MessageBox.Show($"相机操作失败:{ex.Message}", "错误提示");
}

// 4. 图像采集事件处理方法
private void CameraHelper_ImageGrabbed(CogImage8Grey obj)
{
    // 跨线程更新UI,显示采集到的图像
    this.Invoke(new Action(() =>
    {
        _cogDisplay.Image = obj;
        _cogDisplay.Fit(true);
    }));

    // 调用视觉检测方法,处理图像
    _visionHelper.RunDetection(obj);
}

// 5. 窗体关闭时,释放相机资源
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
    cameraHelper?.Dispose();
}

四、 核心总结(工控场景重点提炼)

  1. 变量与数据类型 :优先掌握int/double/bool/string/DateTime,重点练习类型转换(Parse/TryParse),避免空值和类型不匹配异常。
  2. 流程控制if-else是工控首选,for循环用于批量处理,while循环用于等待 / 重试,try-catch必须包裹所有不可控操作(相机、通信、数据库)。
  3. 面向对象:按功能模块封装类(相机 / DB / 视觉),一个方法对应一个单一功能,通过「实例化对象」调用类的方法,实现代码复用和可维护性。
  4. 工控避坑 :变量初始化、避免中文命名、异常处理、资源释放(Dispose/using),这四点是保证程序稳定运行的关键。
相关推荐
咯哦哦哦哦2 小时前
WSL + ubantu22.04 + 远程桌面闪退+黑屏闪退解决
linux·开发语言
翩若惊鸿_2 小时前
【无标题】
开发语言·c#
Da Da 泓2 小时前
多线程(七)【线程池】
java·开发语言·线程池·多线程
搬砖的工人3 小时前
写了一个IIS监控工具,对付“假死“后自动重启站点
c#
杰瑞不懂代码3 小时前
基于 MATLAB 的 BPSK/QPSK/2DPSK 在 AWGN 信道下的 BER 性能仿真与对比分析
开发语言·matlab·qpsk·2psk·2dpsk
小鸡脚来咯3 小时前
python虚拟环境
开发语言·python
龘龍龙3 小时前
Python基础(九)
android·开发语言·python
电摇小人3 小时前
我的“C++之旅”(博客之星主题作文)
java·开发语言
资生算法程序员_畅想家_剑魔3 小时前
Java常见技术分享-23-多线程安全-总结
java·开发语言