c# 基础
值类型 引用类型
引用类型
class object array 接口、委托 等
保存在堆中
clr
管理堆
.net的核心组件 管 代码执行、内存分配、垃圾回收等
object
所有类的基类
C# 中所有类型(值类型 + 引用类型)的最终基类,任何类型都可以隐式转换为object
接口
实现接口时,例如person类 Person tom=new person()
tom是引用类型
csharp
public interface IPerson { }
public class Person : IPerson { }
IPerson tom = new Person(); // 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]");//3
Console.WriteLine("arr1"); //3 2 3
Console.WriteLine(string.Join(" ", arr1));////3 2 3
}
Console.WriteLine("arr1[0]");//3
Console.WriteLine("arr1");// 3 2 3
csharp
using System;
class ArrayRefDemo
{
// 示例1:方法内重新赋值数组变量
static int[] arr1 = { 1, 2, 3 }; // 全局数组
static void Play1()
{
// 这里是「重新创建了一个新数组」,并让方法内的arr1指向新数组
int[] arr1 = { 2, 3, 4 };
Console.WriteLine(string.Join(" ", arr1)); // 输出:2 3 4
}
// 示例2:方法内修改数组的元素
static void Play2()
{
// 这里没有新建数组,而是直接修改「原数组」的第一个元素
arr1[0] = 3;
Console.WriteLine(arr1[0]); // 输出:3
Console.WriteLine(string.Join(" ", arr1)); // 输出:3 2 3
}
static void Main()
{
// 测试示例1
Play1();
Console.WriteLine(string.Join(" ", arr1)); // 输出:1 2 3(原数组没变化)
Console.WriteLine("-----分割线-----");
// 测试示例2
Play2();
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];
csharp
int num = 10;
object j = num; // 装箱:栈的int → 堆的object
int[] arr = new int[2] { 10, 20 };
object obj = arr[0]; // 装箱:arr[0]的int值 → 堆的object
拆箱
object t=5;
int num2=5;
int num2=t;
引用类型变成值类型
csharp
object t = 5;
int num2 = (int)t; // 拆箱:堆的object → 栈的int(必须强转)
数组 array
int[] arr1=new int[1];
float[] arr2=new float[] {1.4f,213.2f,23.1f}
csharp
int[] arr1 = new int[1]; // 声明+初始化(长度1,默认值0)
float[] arr2 = new float[] { 1.4f, 213.2f, 23.1f }; // 声明+初始化(指定元素)
float[] arr3 = { 1.4f, 213.2f }; // 简化写法(编译器自动推断)
泛型 List
可以存多个同类型的数据 长度可以动态变化
csharp
List<int> numList = new List<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 都是 "把变量本身传过去,而不是把值传过去",只是规则不一样。
csharp
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 }; // 改的是原变量的引用
}
Modify2(ref or); // or指向{5,6}
ref
ref :你必须先有,我才能改
void RefMethod(ref int[] arr){
arr[0]=10;
}
int ori=new int[1];
RefMethod(ref ori);//ori[0]:10
csharp
void RefMethod(ref int[] arr)
{
arr[0] = 10; // 改数组内容
}
int[] original = new int[1]; // 必须先初始化
RefMethod(ref original);
我在方法里改 arr[0] = 直接改你外面的数组
ref
外面必须先初始化(你先开好空间)
我方法里帮你赋值 / 改内容
out
你不用有,我来给你造
void OutMethod(out int[] arr){
arr=new int[1];
arr[0]=20;
}
int[] result;
csharp
void OutMethod(out int[] arr)
{
arr = new int[1]; // 必须赋值!
arr[0] = 20;
}
int[] result; // 不用初始化
OutMethod(out result);
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 能让操作更流畅。
csharp
// 异步读取串口数据,UI不卡
private async void btnReadSerial_Click(object sender, EventArgs e)
{
string data = await Task.Run(() =>
{
// 耗时操作:读取串口数据
return serialPort.ReadLine();
});
txtContent.Text = data; // 任务完成后更新UI
}
实例
"实例" 可以理解为根据一个 "模板" 创建出来的具体对象。这里的ModbusSerialMaster.CreateRtu(serialPort)就是用ModbusSerialMaster这个 "模板",结合已打开的串口serialPort,创建出一个能和 Modbus 设备通信的具体 "工具",这个 "工具" 就是master实例,后面读取寄存器、线圈的ReadHoldingRegistersAsync等方法,都是通过这个master实例来调用的
csharp
// ModbusSerialMaster是类(模板)
// master是实例(具体工具),能调用Modbus通信方法
var master = ModbusSerialMaster.CreateRtu(serialPort);
// 调用实例方法:读取保持寄存器
var registers = await master.Read
var
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 会更合适。