c# 基础
值类型 引用类型
引用类型
class object array 接口、委托等
保存在堆中
clr
管理堆
.net的核心组件 管 代码执行、内存分配、垃圾回收等
object
所有类的基类
C# 中所有类型(值类型 + 引用类型)的最终基类,任何类型都可以隐式转换为object
接口
实现接口时,例如person类 Person tom=new person()
tom是引用类型
publicinterfaceIPerson{}publicclassPerson:IPerson{}IPersontom=newPerson();// tom是引用类型(指向堆中Person实例)array
数组 所有数组的基类
创建数组也是引用类型
int[] arr1=new int[2];
arr1[0] 是值类型
引用传递
int[] arr1={1,2,3};
public void play(){
int[] arr1={2,3,4};Console.WriteLine(“arr1”);//2 3 4
Console.WriteLine(string.Join(" “, arr1)); // 输出:2 3 4
}Console.WriteLine(“arr1”)//
Console.WriteLine(string.Join(” ", arr1)); //1 2 3
int arr1={1,2,3};
public void play(){
arr1[0]=3;//arr1[0]=3
Console.WriteLine(“arr1[0]”);//3Console.WriteLine(“arr1”);//3 2 3
Console.WriteLine(string.Join(" ", arr1));////3 2 3
}
Console.WriteLine(“arr1[0]”);//3
Console.WriteLine(“arr1”);// 3 2 3
usingSystem;classArrayRefDemo{// 示例1:方法内重新赋值数组变量staticint[]arr1={1,2,3};// 全局数组staticvoidPlay1(){// 这里是「重新创建了一个新数组」,并让方法内的arr1指向新数组int[]arr1={2,3,4};Console.WriteLine(string.Join(" ",arr1));// 输出:2 3 4}// 示例2:方法内修改数组的元素staticvoidPlay2(){// 这里没有新建数组,而是直接修改「原数组」的第一个元素arr1[0]=3;Console.WriteLine(arr1[0]);// 输出:3Console.WriteLine(string.Join(" ",arr1));// 输出:3 2 3}staticvoidMain(){// 测试示例1Play1();Console.WriteLine(string.Join(" ",arr1));// 输出:1 2 3(原数组没变化)Console.WriteLine("-----分割线-----");// 测试示例2Play2();Console.WriteLine(arr1[0]);// 输出:3(原数组被修改)Console.WriteLine(string.Join(" ",arr1));// 输出:3 2 3}}值类型
包含:int、float、bool、char、结构体(struct)、枚举(enum)等
保存在栈中 例如int
int num=10;
num=15;
num变成15
值传递
int num=15;
public void play(){
int num=10;
Console.WriteLine(“num”);//num=10
}
Console.WriteLine(“num”);//num=15
装箱
int num=10;
object t;
object j=num;
值类型变成引用类型
int [] arr = new int [2] {10, 20};
object obj = arr [0];
intnum=10;objectj=num;// 装箱:栈的int → 堆的objectint[]arr=newint[2]{10,20};objectobj=arr[0];// 装箱:arr[0]的int值 → 堆的object拆箱
object t=5;
int num2=5;
int num2=t;
引用类型变成值类型
objectt=5;intnum2=(int)t;// 拆箱:堆的object → 栈的int(必须强转)数组 array
int[] arr1=new int[1];
float[] arr2=new float[] {1.4f,213.2f,23.1f}
int[]arr1=newint[1];// 声明+初始化(长度1,默认值0)float[]arr2=newfloat[]{1.4f,213.2f,23.1f};// 声明+初始化(指定元素)float[]arr3={1.4f,213.2f};// 简化写法(编译器自动推断)泛型 List
可以存多个同类型的数据 长度可以动态变化
List<int>numList=newList<int>();numList.Add(1);// 动态添加numList.Add(2);numList.Remove(1);// 动态删除ref out
核心:传 “变量本身”(栈中的引用 / 值),而非 “变量的值”
int[] or={1,2};
void Modify(int[] arr){
arr=new int[] {5,6};}
Modify(or);// or指向1,2
void Modify2(ref int[] arr){
arr=new int[] {5,6}
}
Modify(of);// or指向5,6
ref /out 都是 “把变量本身传过去,而不是把值传过去”,只是规则不一样。
int[]or={1,2};voidModify(int[]arr){arr=newint[]{5,6};// 改的是方法内的局部引用,原数组不变}Modify(or);// or仍指向{1,2}voidModify2(refint[]arr){arr=newint[]{5,6};// 改的是原变量的引用}Modify2(refor);// or指向{5,6}ref
ref :你必须先有,我才能改
void RefMethod(ref int[] arr){
arr[0]=10;
}
int ori=new int[1];
RefMethod(ref ori);//ori[0]:10
voidRefMethod(refint[]arr){arr[0]=10;// 改数组内容}int[]original=newint[1];// 必须先初始化RefMethod(reforiginal);我在方法里改 arr[0] = 直接改你外面的数组
ref
外面必须先初始化(你先开好空间)
我方法里帮你赋值 / 改内容
out
你不用有,我来给你造
void OutMethod(out int[] arr){
arr=new int[1];
arr[0]=20;
}
int[] result;
voidOutMethod(outint[]arr){arr=newint[1];// 必须赋值!arr[0]=20;}int[]result;// 不用初始化OutMethod(outresult);out
外面不用初始化(你不用准备)
方法里必须给你初始化(我来开空间、第一次赋值)
ref 是:调用方法之前,我自己先把数组初始化好。out 是:调用之前我不用初始化,但是方法里面必须把数组初始化、赋值好。
声明 初始化 赋值
int[] arr; → 只声明,没初始化
int[] arr = {1,2,3}; → 声明 + 初始化
arr[0] = 10; → 这是赋值
.NET框架
包含:类库(System、System.IO等)、CLR、编译器等
作用:C# 代码运行的基础体系,屏蔽底层系统差异(跨平台
Task
Task 就是帮你 “后台干活” 的工具,比如上位机读取串口数据可能要等设备响应,用 Task 处理这个过程,界面就不会卡住,用户还能拖动窗口、点击按钮
async
async 是 C# 里用来声明异步方法的关键字,和 Task 配合用。比如你写一个异步读取串口数据的方法,就可以加 async 修饰,里面用 await 调用 Task 相关的异步操作,这样方法执行到耗时步骤时不会阻塞主线程。上位机里写界面响应的方法时,用 async 能让操作更流畅。
// 异步读取串口数据,UI不卡privateasyncvoidbtnReadSerial_Click(objectsender,EventArgse){stringdata=awaitTask.Run(()=>{// 耗时操作:读取串口数据returnserialPort.ReadLine();});txtContent.Text=data;// 任务完成后更新UI}实例
“实例” 可以理解为根据一个 “模板” 创建出来的具体对象。这里的ModbusSerialMaster.CreateRtu(serialPort)就是用ModbusSerialMaster这个 “模板”,结合已打开的串口serialPort,创建出一个能和 Modbus 设备通信的具体 “工具”,这个 “工具” 就是master实例,后面读取寄存器、线圈的ReadHoldingRegistersAsync等方法,都是通过这个master实例来调用的
// ModbusSerialMaster是类(模板)// master是实例(具体工具),能调用Modbus通信方法varmaster=ModbusSerialMaster.CreateRtu(serialPort);// 调用实例方法:读取保持寄存器varregisters=awaitmaster.Readvar
var是 “隐式类型”,编译器会根据等号右边的内容自动判断它的具体类型。这里var series01 = new LineSeries(),右边是LineSeries,所以series01实际就是LineSeries类型,和写LineSeries series01 = new LineSeries()效果一样,只是用var写起来更简洁。三条曲线用var定义,本质上都是LineSeries对象,所以能被添加到图表的Series集合里,最终显示成曲线。
enum
enum是枚举类型,用来定义一组有名字的常量,让代码更易读。比如你这项目如果有灯光状态,可定义enum LightState { On=1, Off=0 },代替直接写 1 和 0,别人一看就知道 On 代表开。你代码里控制灯光用bool,如果状态更多,enum 会更合适。