Random类
Random 类用于生成伪随机数,位于 System 命名空间。它的核心机制是基于一个种子值 (seed),通过算法生成看似随机的数列。相同种子会生成相同的随机数序列,这在需要可重现的随机场景中很有用。
//1.1创建一个随机数队列,并指定种子
Random random = new Random(10);//指定种子为10
//1.2获取随机数
Console.WriteLine(random.Next());
//Next(min,max) 会生成一个在min和max之间的随机整数,但是包括min不包括max
Console.WriteLine(random.Next(10,21));
DateTime类
DateTime 表示特定的时间点,精度为 100 纳秒,存储为自 0001 年 1 月 1 日以来的 ticks 数 。它是值类型 (struct),不可变。
DateTime now = DateTime.Now; //现在时间
Console.WriteLine(now.Year); // 年
Console.WriteLine(now.Month); // 月
Console.WriteLine(now.DayOfWeek); // 星期几(枚举)
DateTime tomorrow = DateTime.Now.AddDays(1);
//**比较与差值**
DateTime a = DateTime.Now;
DateTime b = a.AddHours(1);
TimeSpan diff = b - a;
Console.WriteLine(diff.TotalHours); // 输出 1
//时区处理
DateTime local = DateTime.Now;
DateTime utc = local.ToUniversalTime(); // 转换为 UTC
//字符串与 DateTime 互转
DateTime.Parse("2023-10-01"); // 自动解析
DateTime.ParseExact("20231001", "yyyyMMdd", null); // 指定格式
DateTime now = DateTime.Now;
Console.WriteLine(now.ToString("yyyy-MM-dd")); // 输出:2023-10-01
Console.WriteLine(now.ToString("HH:mm:ss")); // 输出:14:30:00
String 类
String 是引用类型,但具有不可变性 (immutable),所有操作都会返回新字符串。它本质是 char 数组的封装。
string s1 = "abc";
string s2 = "吴亦凡";
string s3 = "123";
// string s4 = s1 + s2 + s3;
//1.Concat() 拼接字符串
string s4 = string.Concat(s1, s2, s3);
Console.WriteLine(s4);
//2.Contains() 判断参数字符串 是否出现在源字符串中
//出现 返回true 不出现返回 false
bool b = "wuyifan".Contains("吴");
Console.WriteLine();
//3.CopyTo() 从字符串中复制一部分字符 放到一个字符数组中
//参数1:被复制的字符串开始的索引
//参数2:复制到的数组
//参数3:从数组中第几个索引位置开始放
//参数4:复制的个数
s2.CopyTo(1, chars, 0, 2);
//4.ToUpper() 将小写字母 转成大写字母
Console.WriteLine(s5.ToUpper());//ABDDD
//5.ToLower() 将大写字母 转成小写字母
Console.WriteLine(s5.ToLower());//abddd
//7.Replace() 替换字符
| 方法名称 | 说明 | 示例代码 | 执行结果 |
|---|---|---|---|
Length(属性) |
获取字符串长度 | string s = "Hello"; int len = s.Length; |
5 |
ToUpper() |
转换为大写字母 | string s = "Hello"; string res = s.ToUpper(); |
"HELLO" |
ToLower() |
转换为小写字母 | string s = "HELLO"; string res = s.ToLower(); |
"hello" |
Trim() |
移除首尾空白字符 | string s = " Hello "; string res = s.Trim(); |
"Hello" |
TrimStart() |
移除开头空白字符 | string s = " Hello"; string res = s.TrimStart(); |
"Hello" |
TrimEnd() |
移除结尾空白字符 | string s = "Hello "; string res = s.TrimEnd(); |
"Hello" |
Contains(string) |
判断是否包含指定子串 | string s = "Hello World"; bool res = s.Contains("World"); |
true |
StartsWith(string) |
判断是否以指定子串开头 | string s = "Hello"; bool res = s.StartsWith("He"); |
true |
EndsWith(string) |
判断是否以指定子串结尾 | string s = "Hello"; bool res = s.EndsWith("llo"); |
true |
IndexOf(char) |
查找字符首次出现的索引(-1 表示未找到) | string s = "Hello"; int res = s.IndexOf('l'); |
2 |
LastIndexOf(char) |
查找字符最后出现的索引 | string s = "Hello"; int res = s.LastIndexOf('l'); |
3 |
Substring(int, int) |
从指定索引截取指定长度的子串 | string s = "Hello World"; string res = s.Substring(6, 5); |
"World" |
Replace(string, string) |
替换指定子串为新字符串 | string s = "Hello"; string res = s.Replace("l", "x"); |
"Hexxo" |
Split(char[]) |
按指定字符分割为字符串数组 | string s = "Apple,Banana,Orange"; string[] res = s.Split(','); |
["Apple", "Banana", "Orange"] |
Join(string, string[]) |
将字符串数组拼接为一个字符串 | string[] arr = { "A", "B", "C" }; string res = string.Join("-", arr); |
"A-B-C" |
IsNullOrEmpty(string) |
判断字符串是否为null或空串 |
bool res1 = string.IsNullOrEmpty(""); bool res2 = string.IsNullOrEmpty(null); |
true, true |
IsNullOrWhiteSpace(string) |
判断是否为null、空串或仅含空白 |
bool res = string.IsNullOrWhiteSpace(" "); |
true |
Insert(int, string) |
在指定索引插入子串 | string s = "Hello"; string res = s.Insert(5, " World"); |
"Hello World" |
Remove(int, int) |
从指定索引移除指定长度的字符 | string s = "Hello World"; string res = s.Remove(5, 1); |
I/O 类(System.IO 命名空间)
-
文件:存储在磁盘中,带有指定名称和路径的数据集合。
-
流(Stream):文件读写时的字节序列,分为:
-
输入流:从文件读取数据(读操作)
-
输出流:向文件写入数据(写操作)
-
// 读文件
using (FileStream fs = new FileStream("test.txt", FileMode.Open, FileAccess.Read)) {
byte[] buffer = new byte[1024];
int length = fs.Read(buffer, 0, buffer.Length); // 读取字节
string content = Encoding.UTF8.GetString(buffer, 0, length); // 解码
}
// 写文件
using (FileStream fs = new FileStream("test.txt", FileMode.Create, FileAccess.Write)) {
byte[] data = Encoding.UTF8.GetBytes("Hello"); // 编码
fs.Write(data, 0, data.Length); // 写入字节
}
// 读文件
using (FileStream fs = new FileStream("test.txt", FileMode.Open, FileAccess.Read)) {
byte[] buffer = new byte[1024];
int length = fs.Read(buffer, 0, buffer.Length); // 读取字节
string content = Encoding.UTF8.GetString(buffer, 0, length); // 解码
}
// 写文件
using (FileStream fs = new FileStream("test.txt", FileMode.Create, FileAccess.Write)) {
byte[] data = Encoding.UTF8.GetBytes("Hello"); // 编码
fs.Write(data, 0, data.Length); // 写入字节
}
//BufferedStream(带缓冲区的流)
using (FileStream fs = new FileStream("bigfile.txt", FileMode.Create))
using (BufferedStream bs = new BufferedStream(fs)) {
byte[] data = Encoding.UTF8.GetBytes("大量数据...");
bs.Write(data, 0, data.Length); // 先写入缓冲区,满了自动刷到文件
bs.Flush(); // 手动刷新缓冲区(确保数据写入文件)
}
File 类
| 方法 | 作用 |
|---|---|
Create(string path) |
创建文件,返回FileStream |
Delete(string path) |
删除文件 |
Move(string src, string dest) |
移动文件 |
Copy(string src, string dest) |
复制文件 |
WriteAllText(string path, string content) |
写入字符串(覆盖原内容) |
ReadAllText(string path) |
读取所有内容为字符串 |
WriteAllLines(string path, string[] lines) |
按行写入字符串数组 |
// 定义测试文件路径(相对路径,会创建在程序运行目录)
string filePath = @"test_file.txt";
string copyPath = @"test_file_copy.txt"; // 复制目标路径
string movePath = @"moved_test_file.txt"; // 移动目标路径
// 1. 创建文件并写入内容(覆盖式写入)
// WriteAllText:若文件不存在则创建,存在则覆盖原有内容
string content = "这是第一行内容\n这是第二行内容";
File.WriteAllText(filePath, content);
Console.WriteLine($"1. 已创建文件并写入内容:{filePath}");
// 2. 追加内容到文件
// AppendAllText:在文件末尾添加内容(文件不存在则创建)
string appendContent = "\n这是追加的内容";
File.AppendAllText(filePath, appendContent);
Console.WriteLine("2. 已向文件追加内容");
// 3. 读取文件所有内容(字符串形式)
string allText = File.ReadAllText(filePath);
Console.WriteLine("\n3. 读取文件所有内容:");
Console.WriteLine(allText);
// 4. 按行读取文件内容
string[] lines = File.ReadAllLines(filePath);
Console.WriteLine("\n4. 按行读取文件内容:");
for (int i = 0; i < lines.Length; i++)
{
Console.WriteLine($"第{i+1}行:{lines[i]}");
}
// 5. 以字节形式读取文件(适用于二进制文件)
byte[] bytes = File.ReadAllBytes(filePath);
Console.WriteLine($"\n5. 文件字节长度:{bytes.Length} 字节");
// 6. 复制文件
// 第三个参数为true表示允许覆盖已存在的目标文件
File.Copy(filePath, copyPath, overwrite: true);
Console.WriteLine($"\n6. 已复制文件到:{copyPath}");
// 7. 移动文件(剪切)
File.Move(copyPath, movePath);
Console.WriteLine($"7. 已移动文件到:{movePath}");
// 8. 检查文件是否存在
bool exists = File.Exists(filePath);
Console.WriteLine($"\n8. 文件 {filePath} 是否存在:{exists}");
// 9. 删除文件
File.Delete(movePath);
Console.WriteLine($"9. 已删除文件:{movePath}");
StreamReader / StreamWriter(文本文件操作)
// 写文本文件
using (StreamWriter sw = new StreamWriter("log.txt")) {
sw.WriteLine("第一行");
sw.WriteLine("第二行");
}
// 读文本文件
using (StreamReader sr = new StreamReader("log.txt")) {
string line;
while ((line = sr.ReadLine()) != null) { // 逐行读取
Console.WriteLine(line);
}
}
DriveInfo(驱动器信息)
-
AvailableFreeSpace:可用空间(字节) -
DriveFormat:文件系统(如 NTFS) -
DriveType:驱动器类型(固定 / 移动 / CD-ROM 等) -
IsReady:驱动器是否就绪
DriveInfo cDrive = new DriveInfo("C:");
if (cDrive.IsReady) {
Console.WriteLine($"可用空间:{cDrive.AvailableFreeSpace / 1024 / 1024} MB");
Console.WriteLine($"文件系统:{cDrive.DriveFormat}");
}
文件属性操作(File / FileInfo)
FileInfo file = new FileInfo("test.txt");
file.Attributes = FileAttributes.ReadOnly | FileAttributes.Hidden; // 设置只读+隐藏
Console.WriteLine($"是否只读:{file.IsReadOnly}");
Console.WriteLine($"创建时间:{file.CreationTime}");
-
Path 类:处理路径字符串(跨平台兼容)。
-
GetDirectoryName(path):获取目录 -
GetExtension(path):获取扩展名 -
Combine(path1, path2):拼接路径 -
GetFullPath(relativePath):获取绝对路径
-
-
Environment 类:提供系统环境信息。
-
CurrentDirectory:当前工作目录 -
GetFolderPath(SpecialFolder):获取特殊文件夹路径(如桌面、临时目录)
-
目录操作(Directory / DirectoryInfo)
-
Directory:静态类,适合单次目录操作。 -
DirectoryInfo:非静态类,适合多次操作同一目录。
常用方法
-
CreateDirectory(path):创建目录(含多级) -
Delete(path, recursive):删除目录(recursive=true删除非空目录) -
GetFiles(path):获取目录下所有文件 -
GetDirectories(path):获取子目录
递归遍历目录示例
static void TraverseDir(string path) {
foreach (string dir in Directory.GetDirectories(path)) {
Console.WriteLine($"目录:{dir}");
foreach (string file in Directory.GetFiles(dir)) {
Console.WriteLine($"文件:{file}");
}
TraverseDir(dir); // 递归遍历子目录
}
}
C#Encoding
System.Text.Encoding 是 C# 中处理字符编码与转换的核心类,核心作用是:
-
建立字符串(字符序列) 与字节数组(二进制数据) 之间的转换桥梁
-
实现不同编码格式(如 UTF-8、ASCII 等)的映射规则,解决 "人类可识别字符" 与 "计算机存储的二进制" 之间的转换问题
1. 网络通信(确保跨系统一致性)
// 发送方:字符串→UTF8字节
string sendText = "Hello,世界";
byte[] sendBytes = Encoding.UTF8.GetBytes(sendText);
// 接收方:UTF8字节→字符串
byte[] receiveBytes = ...; // 从网络接收的字节
string receiveText = Encoding.UTF8.GetString(receiveBytes);
2. 文件读写(匹配文件编码)
// 写入UTF8编码文件(无BOM)
string content = "中文内容";
byte[] fileBytes = Encoding.UTF8.GetBytes(content);
File.WriteAllBytes("file.txt", fileBytes);
// 读取时用相同编码
byte[] readBytes = File.ReadAllBytes("file.txt");
string readContent = Encoding.UTF8.GetString(readBytes);
3. 多编码转换(如 GBK→UTF8)
// 假设bytes是GBK编码的字节
byte[] gbkBytes = ...;
// 步骤1:用GBK解码为字符串
string text = Encoding.GetEncoding("GBK").GetString(gbkBytes);
// 步骤2:用UTF8重新编码为字节
byte[] utf8Bytes = Encoding.UTF8.GetBytes(text);
JSON 序列化
-
序列化:将对象状态转换为可存储(如文件)或可传输(如网络)的格式(二进制、XML、JSON 等)的过程。
-
反序列化:将序列化后的格式(如二进制流、JSON 文本)还原为对象的过程。
JSON(JavaScript Object Notation)是一种轻量级文本格式,易读易写,跨平台兼容性强(支持多语言),是目前最常用的序列化格式之一。
第三方Newtonsoft.Json(推荐)
使用步骤
-
安装库:项目右键→"管理 NuGet 程序包"→搜索 "Newtonsoft.Json"→安装;
-
引入命名空间 :
using Newtonsoft.Json;; -
序列化 :调用
JsonConvert.SerializeObject(对象)生成 JSON 字符串,写入文件; -
反序列化 :读取 JSON 字符串,调用
JsonConvert.DeserializeObject<类型>(字符串)还原对象。
示例代码
using Newtonsoft.Json;
// 1. 定义普通类(无需任何特性)
public class People
{
public string Name { get; set; }
public int Age { get; set; }
public string Sex { get; set; }
public string Birth { get; set; }
}
// 2. 序列化
public void JsonSerialize_Newtonsoft()
{
People people = new People()
{
Name = "吴亦凡",
Age = 18,
Sex = "男",
Birth = "2005-01-01"
};
// 转换对象为JSON字符串
string jsonStr = JsonConvert.SerializeObject(people);
// 写入文件
File.WriteAllText("people.json", jsonStr);
}
// 3. 反序列化
public void JsonDeserialize_Newtonsoft()
{
// 读取JSON字符串
string jsonStr = File.ReadAllText("people.json");
// 转换为对象
People people = JsonConvert.DeserializeObject<People>(jsonStr);
Console.WriteLine($"姓名:{people.Name},年龄:{people.Age}");
}
CSV 文件读写
输入示例代码:
using System;
using System.Collections.Generic;
using System.IO;
// 定义数据模型
public class Student
{
public string Name { get; set; }
public int Age { get; set; }
public string Gender { get; set; }
}
// 写入CSV文件
public void WriteCsv()
{
// 准备数据
List<Student> students = new List<Student>
{
new Student { Name = "小明", Age = 20, Gender = "男" },
new Student { Name = "小红", Age = 18, Gender = "女" },
new Student { Name = "Doe, John", Age = 22, Gender = "男" } // 包含逗号的特殊字段
};
// 定义CSV文件路径
string filePath = "students.csv";
// 写入文件
using (StreamWriter sw = new StreamWriter(filePath, false, System.Text.Encoding.UTF8))
{
// 写入表头
sw.WriteLine("姓名,年龄,性别");
// 写入数据行
foreach (var student in students)
{
// 处理包含逗号的字段:用双引号包裹
string name = student.Name.Contains(",") ? $"\"{student.Name}\"" : student.Name;
// 拼接行(字段用逗号分隔)
string line = $"{name},{student.Age},{student.Gender}";
sw.WriteLine(line);
}
}
Console.WriteLine("CSV文件写入完成!");
}
生成的students.csv内容:
姓名,年龄,性别
小明,20,男
小红,18,女
"Doe, John",22,男 # 特殊字段被双引号包裹
输出示例代码:
// 读取CSV文件并转换为对象列表
public List<Student> ReadCsv()
{
List<Student> students = new List<Student>();
string filePath = "students.csv";
using (StreamReader sr = new StreamReader(filePath, System.Text.Encoding.UTF8))
{
// 读取表头(跳过,或可用于验证列名)
string header = sr.ReadLine();
// 读取数据行
string line;
while ((line = sr.ReadLine()) != null)
{
// 处理带双引号的字段(简单处理:移除引号)
line = line.Replace("\"", "");
// 按逗号分割字段
string[] fields = line.Split(',');
// 验证字段数量(确保与表头对应)
if (fields.Length != 3)
{
Console.WriteLine($"跳过无效行:{line}");
continue;
}
// 转换为Student对象
students.Add(new Student
{
Name = fields[0],
Age = int.Parse(fields[1]), // 字符串→整数
Gender = fields[2]
});
}
}
return students;
}
// 使用示例
var studentList = ReadCsv();
foreach (var s in studentList)
{
Console.WriteLine($"姓名:{s.Name},年龄:{s.Age},性别:{s.Gender}");
}
输出结果:
姓名:小明,年龄:20,性别:男
姓名:小红,年龄:18,性别:女
姓名:Doe, John,年龄:22,性别:男 # 正确解析带逗号的字段
INI 文件操作
INI 文件是一种轻量级的配置文件格式,主要用于 Windows 应用程序存储配置信息(如窗口设置、数据库连接、设备参数等)。它以文本形式存在,结构简单,易于人工编辑和维护,适用于不需要复杂数据类型或层级结构的场景。
1. 基本结构
[节名] ; 节(用方括号包裹,区分不同配置组)
键1=值1 ; 键值对(键与值用等号连接)
键2=值2
...
[另一节名] ; 支持多个节(多层级节)
键A=值A
键B=值B
2. 示例
[Database] ; 数据库配置节
Server=127.0.0.1 ; 数据库服务器地址
Port=3306 ; 端口号
Username=root ; 用户名
Password=123456 ; 密码
[Logging] ; 日志配置节
Level=INFO ; 日志级别
File=/var/log/myapp.log ; 日志路径
[相机1] ; 设备配置节(支持中文节名)
曝光=50 ; 曝光参数
亮度=100 ; 亮度参数
相机ip=192.168.1.100 ; IP地址
添加必要引用
- 需引用
System.Configuration命名空间(用于读取App.config配置),若项目中未包含,需手动添加: 右键项目→"添加"→"引用"→勾选 "System.Configuration"。
工具类完整代码
using System;
using System.Collections.Specialized;
using System.Configuration;
using System.Text;
using System.Runtime.InteropServices;
internal class FileIni
{
// 从App.config中读取INI文件路径,并添加空引用检查
private static readonly NameValueCollection iniSettings = ConfigurationManager.GetSection("IniFileSettings") as NameValueCollection;
private static readonly string filePath = GetIniFilePath();
// 获取INI文件路径并处理可能的空值
private static string GetIniFilePath()
{
if (iniSettings == null)
{
throw new ConfigurationErrorsException("未找到IniFileSettings配置节,请检查App.config");
}
string path = iniSettings["FilePath"];
if (string.IsNullOrEmpty(path))
{
throw new ConfigurationErrorsException("IniFileSettings配置节中未设置有效的FilePath");
}
return path;
}
// 导入kernel32.dll的宽字符版本函数(支持Unicode/UTF-16)
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool WritePrivateProfileStringW(
string lpAppName, // 节名
string lpKeyName, // 键名
string lpString, // 值(UTF-16)
string lpFileName // INI文件路径
);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
private static extern uint GetPrivateProfileStringW(
string lpAppName, // 节名
string lpKeyName, // 键名
string lpDefault, // 默认值
StringBuilder lpReturnedString, // 存储结果的字符串构建器
uint nSize, // 结果缓冲区大小
string lpFileName // INI文件路径
);
/// <summary>
/// 向INI文件写入UTF-8编码的键值对
/// </summary>
public static void Write(string section, string key, string value)
{
if (string.IsNullOrEmpty(section))
throw new ArgumentNullException(nameof(section));
if (string.IsNullOrEmpty(key))
throw new ArgumentNullException(nameof(key));
// 将UTF-8字符串转换为API所需的UTF-16
WritePrivateProfileStringW(section, key, value, filePath);
}
/// <summary>
/// 从INI文件读取UTF-8编码的键值
/// </summary>
public static string Read(string section, string key)
{
if (string.IsNullOrEmpty(section))
throw new ArgumentNullException(nameof(section));
if (string.IsNullOrEmpty(key))
throw new ArgumentNullException(nameof(key));
StringBuilder sb = new StringBuilder(1024); // 增大缓冲区
GetPrivateProfileStringW(section, key, "", sb, (uint)sb.Capacity, filePath);
// 结果已经是UTF-16,直接返回(.NET内部字符串为UTF-16)
return sb.ToString();
}
/// <summary>
/// 读取指定节的所有键值对
/// </summary>
public static NameValueCollection ReadSection(string section)
{
if (string.IsNullOrEmpty(section))
throw new ArgumentNullException(nameof(section));
// 先获取所有键名
StringBuilder keysSb = new StringBuilder(4096);
GetPrivateProfileStringW(section, null, "", keysSb, (uint)keysSb.Capacity, filePath);
NameValueCollection result = new NameValueCollection();
string[] keys = keysSb.ToString().Split(new[] { '\0' }, StringSplitOptions.RemoveEmptyEntries);
foreach (string key in keys)
{
result.Add(key, Read(section, key));
}
return result;
}
}
使用工具类操作 INI 文件
(1)写入配置
// 向[相机1]节写入"曝光"和"亮度"
FileIni.Write("相机1", "曝光", "50");
FileIni.Write("相机1", "亮度", "100");
// 向[Database]节写入"Server"
FileIni.Write("Database", "Server", "192.168.1.1");
(2)读取配置
// 读取[相机1]节的"曝光"值
string exposure = FileIni.Read("相机1", "曝光"); // 返回"50"
// 读取[Database]节的"Server"值
string dbServer = FileIni.Read("Database", "Server"); // 返回"192.168.1.1"
// 转换数据类型(因INI值均为字符串)
int exposureValue = int.Parse(exposure); // 转换为整数50
C# 反射(Reflection)
本质 :程序运行时动态查看、操作自身或其他程序集元数据(类、方法、属性、字段等类型信息)的机制,基于程序集中存储的元数据实现。
// 加载指定路径的程序集
Assembly assembly = Assembly.LoadFile(@"F:\libs\ClassLibrary1.dll");
Type studentType = assembly.GetType("_02_反射.Student"); // 从程序集获取
object student = assembly.CreateInstance("_02_反射.Student"); // 无参构造
FieldInfo idField = studentType.GetField("myId"); // 获取公开字段
PropertyInfo nameProp = studentType.GetProperty("Name");//获取属性
MethodInfo method = studentType.GetMethod("Method2");//获取方法
method.Invoke(student, new object[] { "参数1", "参数2" }); // 调用方法,记得转换类型
textBox2.Text = encryptMethod.Invoke(instance, new object[] { textBox1.Text }) as string; //赋值给对应控件
重要反射 API 汇总
| API | 作用 | 返回类型 |
|---|---|---|
typeof(类型) |
编译时获取类型信息 | Type |
实例.GetType() |
运行时通过实例获取类型信息 | Type |
Assembly.Load(名称) |
加载程序集(基于程序集名称) | Assembly |
Assembly.LoadFile(路径) |
加载指定路径的程序集 | Assembly |
Type.GetField(名称) |
获取字段信息 | FieldInfo |
Type.GetProperty(名称) |
获取属性信息 | PropertyInfo |
Type.GetMethod(名称) |
获取方法信息 | MethodInfo |
Type.GetConstructor() |
获取构造函数信息 | ConstructorInfo |
FieldInfo.SetValue() |
设置字段值 | void |
PropertyInfo.GetValue() |
获取属性值 | object |
MethodInfo.Invoke() |
调用方法 | object |
Type.GetCustomAttribute() |
获取类型上的特性 | 特性类型实例 |
C# 特性(Attribute)
C#单例模式
设计模式是解决软件开发特定问题的成熟解决方案 ,核心目标是通过规范化的设计思路,实现代码的高内聚 (模块专注单一功能)和低耦合(减少模块间依赖),从而提升代码的可重用性、可维护性和灵活性。
单例模式:确保一个类只有一个实例,并提供一个全局访问点。
多线程
//线程创建
// 直接传入方法(推荐,最简洁)
Thread thread4 = new Thread(ThreadFun1);
//直接嵌入匿名方法(推荐,适合简单逻辑)
Thread thread5 = new Thread(() => Console.WriteLine("分线程5"));
//传递参数
Thread threadC = new Thread(ThreadFun2);
threadC.Start("参数C"); // 最简洁的传参方式
Thread threadC = new Thread(ThreadFun2);threadC.Start("参数C"); // 最简洁的传参方式
//线程状态流转过程
Thread thread = new Thread(() => {
Thread.Sleep(1000); // 触发WaitSleepJoin状态
});
Console.WriteLine(thread.ThreadState); // Unstarted(创建后未启动)
thread.Start();
Console.WriteLine(thread.ThreadState); // 可能为Running或WaitSleepJoin
thread.Join();
Console.WriteLine(thread.ThreadState); // Stopped(执行完毕)
线程池(ThreadPool)
线程池(ThreadPool)是.NET Framework 2.0 引入的池化技术 实现,核心思想是:预先创建一定数量的线程并维护在 "池" 中,当程序需要使用线程时直接从池中获取,使用完毕后线程不销毁而是放回池中等待复用,由公共语言运行时(CLR)和垃圾回收器(GC)自动管理线程的生命周期。
// 直接嵌入匿名方法,无需单独定义回调
ThreadPool.QueueUserWorkItem(state =>
{
string msg = state as string;
Console.WriteLine($"匿名方法执行:{msg},线程ID:{Thread.CurrentThread.ManagedThreadId}");
}, "Hello ThreadPool");
Task 任务
Task 是.NET 4.0 推出的任务类,基于线程池封装,是对线程池功能的增强与扩展。相比 Thread 和 ThreadPool,Task 提供了更丰富的控制能力(如任务取消、延续、等待、超时管理等),且 API 更简洁,是.NET 多线程编程的推荐方案。
//无返回值任务
// 方式1:Task.Run(推荐,直接执行任务)
Task task1 = Task.Run(() => {
Console.WriteLine("Task1执行中...");
Thread.Sleep(1000); // 模拟任务耗时
});
//有返回值任务
// 方式1:Task.Run<TResult>
Task<int> taskWithResult = Task.Run(() => {
Console.WriteLine("计算中...");
Thread.Sleep(1000);
return 100 + 200; // 返回结果(int类型)
});
// 后续可通过taskWithResult.Result获取结果
//任务取消(CancellationTokenSource)
// 1. 创建取消源
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken token = cts.Token;
// 2. 启动一个可取消的任务
Task task = Task.Run(() => {
for (int i = 0; i < 10; i++) {
// 检查是否收到取消信号(协作式取消的核心)
if (token.IsCancellationRequested) {
Console.WriteLine("任务已取消!");
return; // 收到信号后退出任务
}
Console.WriteLine($"任务执行中:{i}");
Thread.Sleep(500); // 模拟耗时
}
Console.WriteLine("任务正常完成!");
}, token); // 将令牌传入任务
// 3. 3秒后发出取消信号
Thread.Sleep(3000);
cts.Cancel(); // 触发取消
// 等待任务结束
task.Wait();
//延迟执行:Task.Delay
// 阻塞式延迟(主线程会卡住1秒)
Console.WriteLine("开始延迟...");
Task.Delay(1000).Wait();
Console.WriteLine("延迟结束(阻塞式)");
// 非阻塞式延迟(需在async方法中)
async static void TestDelay() {
Console.WriteLine("开始非阻塞延迟...");
await Task.Delay(1000); // 主线程不阻塞,可继续执行其他操作
Console.WriteLine("延迟结束(非阻塞式)");
}
TestDelay();
Console.WriteLine("主线程继续执行..."); // 会先于"延迟结束"输出
//单个任务等待:Task.Wait ()
Task task = Task.Run(() => {
Thread.Sleep(2000);
Console.WriteLine("任务完成");
});
Console.WriteLine("等待任务完成...");
task.Wait(); // 主线程阻塞2秒,直到任务完成
Console.WriteLine("主线程继续");
//多个任务等待:WaitAny 与 WaitAll(阻塞式)
Task[] tasks = {
Task.Run(() => { Thread.Sleep(1000); Console.WriteLine("任务1完成"); }),
Task.Run(() => { Thread.Sleep(2000); Console.WriteLine("任务2完成"); }),
Task.Run(() => { Thread.Sleep(1500); Console.WriteLine("任务3完成"); })
};
// 等待任意一个任务完成
int firstIndex = Task.WaitAny(tasks);
Console.WriteLine($"第一个完成的任务索引:{firstIndex}"); // 输出0(任务1先完成)
// 等待所有任务完成
Task.WaitAll(tasks);
Console.WriteLine("所有任务均已完成");
//多个任务等待:WhenAny 与 WhenAll(非阻塞式)适合异步场景。
async static void TestWhen() {
Task[] tasks = {
Task.Run(() => { Thread.Sleep(1000); return "任务1结果"; }),
Task.Run(() => { Thread.Sleep(2000); return "任务2结果"; })
};
// 等待任意任务完成(非阻塞)
Task firstCompleted = await Task.WhenAny(tasks);
Console.WriteLine($"第一个完成的任务结果:{(firstCompleted as Task<string>).Result}"); // 任务1结果
// 等待所有任务完成(非阻塞)
await Task.WhenAll(tasks);
Console.WriteLine("所有任务完成");
}
TestWhen();
//获取任务结果(Task<TResult>与 Result)
// 创建带返回值的任务
Task<int> calcTask = Task.Run(() => {
Console.WriteLine("计算1+2+...+100...");
Thread.Sleep(1000);
int sum = 0;
for (int i = 1; i <= 100; i++) sum += i;
return sum; // 返回计算结果
});
// 方式1:通过Result获取(阻塞式,主线程等待)
Console.WriteLine("等待结果...");
int result = calcTask.Result; // 阻塞直到任务完成
Console.WriteLine($"结果:{result}"); // 输出5050
// 方式2:通过await获取(非阻塞式,需在async方法中)
async static void GetResultAsync() {
int result = await calcTask; // 非阻塞等待
Console.WriteLine($"异步获取结果:{result}");
}
GetResultAsync();