导图社区 CSharp语言基础知识重点系统笔记
介绍归纳 C Sharp 语音的基础重点知识。包括语言基础、字面常量、程序集、不安全代码、基础类、枚举、数组、泛型、字符串、正则表达式、委托与事件、文件、异常、多线程、异步、反射、网络、绘图、WinForm、Windows、跨平台调用等内容。思维导图示例中,有示例代码,方便学习与练习。
编辑于2023-06-21 08:14:37 广东介绍归纳 C Sharp 语音的基础重点知识。包括语言基础、字面常量、程序集、不安全代码、基础类、枚举、数组、泛型、字符串、正则表达式、委托与事件、文件、异常、多线程、异步、反射、网络、绘图、WinForm、Windows、跨平台调用等内容。思维导图示例中,有示例代码,方便学习与练习。
这份思维导图归纳了一些HTML基本的元素标签、布局、表单,以及 HTML5 API 如 WebSockets、Fetch API 等内容。CSS 主要是归纳了选择器。JavaScript 主要是包含了函数与箭头函数、this 关键字、Promise 异步对象。此外还有AJAX、jQuery 与 jQuery AJAX、JSONP 等内容。导图中的注释有很多相关的详细说明与示例代码,其中后端的测试代码是用的 PHP。希望能帮到大家!
WPF开发相关的笔记。WPF基本概念、XAML基本语法、控件与布局、Binding、依赖属性与附加属性、路由事件与附加事件、命令、资源、模板与样式、2D绘图与动画、3D绘图等内容。导图中的注释还有很多相关的详细说明与示例代码,希望能帮到大家!
社区模板帮助中心,点此进入>>
介绍归纳 C Sharp 语音的基础重点知识。包括语言基础、字面常量、程序集、不安全代码、基础类、枚举、数组、泛型、字符串、正则表达式、委托与事件、文件、异常、多线程、异步、反射、网络、绘图、WinForm、Windows、跨平台调用等内容。思维导图示例中,有示例代码,方便学习与练习。
这份思维导图归纳了一些HTML基本的元素标签、布局、表单,以及 HTML5 API 如 WebSockets、Fetch API 等内容。CSS 主要是归纳了选择器。JavaScript 主要是包含了函数与箭头函数、this 关键字、Promise 异步对象。此外还有AJAX、jQuery 与 jQuery AJAX、JSONP 等内容。导图中的注释有很多相关的详细说明与示例代码,其中后端的测试代码是用的 PHP。希望能帮到大家!
WPF开发相关的笔记。WPF基本概念、XAML基本语法、控件与布局、Binding、依赖属性与附加属性、路由事件与附加事件、命令、资源、模板与样式、2D绘图与动画、3D绘图等内容。导图中的注释还有很多相关的详细说明与示例代码,希望能帮到大家!
C#
语言基础
.NET Framework 架构
图示

C# 程序的编译与执行
图示

应用
.NET Framework 控制台程序
初始启动代码
启动类设置位置
可执行程序集->属性->启动对象
例:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleApp1 { internal class Program { static void Main(string[] args) { } } }
.NET Core 控制台程序
初始启动代码
例:
using System; namespace ConsoleApp1 { internal class Program { static void Main(string[] args) { } } }
数据类型
值类型
变量直接包含数据
存储在内存栈
分类
结构体
枚举
引用类型
变量只包含对象的引用
存储在内存栈
对象的数据存储在内存堆
分类
类
接口
委托
类
三大成员
属性
模型类或对象重在属性
方法
工具类或对象重在属性
事件
通知类或对象重在属性
变量
类型
推断类型
根据被赋给值的类型推断变量的类型
关键字 var
例:
var a = 0.0;
标识符
规范
开头
字母、下划线、@、普通 Unicode 字符
@可以使保留字也可以用于标识符
例: @class
字面常量
前缀
0x或0X
十六进制数
0b或0B
C#7.0开始支持
二进制数
\u + 4位16进制数
'\u000A'
Unicode码
后缀
u或U
无符号整数类型
l或L
64bit有符号整数类型
f或F
单精度浮点数
d或D
双精度浮点数
实数类型
浮点数指数形式
5.3e-2表示0.053,123E3表示123000
m或M
精确十进制数
注释
//
单行注释
//*...*//
多行注释
///...
文档注释,常采用 XML 格式
命名空间
是多个类的逻辑组织
定义
namespace 命名 {}
命名可以是1个标识符,或由 . 隔开的多个标识符
导入
导入命名空间
using 命名空间;
导入静态类
using static 静态类完整类名;
带命名空间的类名。
可以省略类名而直接写方法名
using static指令需要添加到文件顶部,即放在namespace之前,这样每次使用System.Console类的成员,都不需要再添加System.Console前缀。相反,直接使用其中的方法名即可。 注意该指令只支持静态方法和属性,不支持实例成员。
例:
using static System.Console; namespace ListNode { class HelloWorld { static void Main() { string firstname; string lastname; WriteLine("Hey you!"); Write("Enter your first name:"); firstname = ReadLine(); Write("Enter your last name:"); lastname = ReadLine(); WriteLine($"Your full name is {firstname} {lastname}"); } } }
声明别名
using 别名 = 命名空间或完整类名;
解决多个命名空间的同名问题
装箱拆箱
装箱
把栈上的值类型的值封装成堆上的引用类型的值
拆箱
把堆上的引用类型的值转换成栈上的值类型的值
例:
int num = 10; //值类型 object objNum = num; //装箱 num = Convert.ToInt32(objNum); //Convert 转换 拆箱
预处理指令
标识符声明
指令
#define 标识符、#undef 标识符
条件处理
指令
#if、#elif、#else、#endif
表达式
标识符、常量true、false
!、==、!=、&&、||
信息报告
指令
#warning 信息、#error 信息
行号标记
指令
#line 行号 "文件名"
代码块
指令
#region、#endregion
程序集
是对类进行的物理组织
分类
模块(module),文件后缀(*.mod)
定义
源代码编译后的(MSIL)指令存放的文件
命令
从源生成
csc /target:module /out:模块文件名.mod 源文件1.cs 源文件2.cs
程序集(assembly),文件后缀(*.dll)
定义
由模块、资源组成
程序指令; 关于指令、资源等的描述信息(元信息 manifest);
命令
从源生成
csc /target:library /out:程序集文件名.dll 源文件1.cs 源文件2.cs
同时添加模块
csc /target:library /out:程序集文件名.dll /addmodule:模块文件1.mod;模块文件2.mod 源文件1.cs 源文件2.cs
从模块生成
csc /target:library /out:程序集文件名.dll 模块文件1.mod 模块文件2.mod
应用程序(application),文件后缀(*.exe)
定义
由程序集组成
命令
从源生成
控制台
csc /target:exe /out:程序文件名.exe 源文件1.cs 源文件2.cs
引用程序集
csc /target:exe /out:程序文件名.exe /reference:程序集文件1.dll /r:程序集文件2.dll 源文件1.cs 源文件2.cs
窗体
csc /target:win /out:程序文件名.exe 源文件1.cs 源文件2.cs
查看程序集的中间语言代码
打开 Developer PowerShell for VS 工具;输入 ildasm 命令;在 IL DASM 打开程序集,然后双击要看的方法;
程序集的 .config 配置
获取当前exe配置
Configuration config = System.Configuration.ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
获取指定exe配置
Configuration config = ConfigurationManager.OpenExeConfiguration(string exePath)
获取当前exe配置节指定键值
string value = ConfigurationManager.AppSettings[key];
获取配置指定键值
string value = config.AppSettings.Settings[key].Value;
不安全代码
概念
即非托管的、允许使用指针的代码
不安全上下文
指定关键字 unsafe
用于结构、类、方法、属性、委托、语句等
编译
需制定 /unsafe 选项(程序集生成中勾选“允许不安全代码”)
指针
声明
类型* 指针
与 C 和 c + + 不同,在 c # 中声明多个指针时, * 仅与基础类型一起编写,而不是作为每个指针名称的前缀标点符号。 例: int* pi, pj; 与引用(引用类型的值)不同,垃圾收集器不跟踪指针——垃圾收集器不知道指针及其指向的数据。因此,不允许指针指向引用或指向包含引用的结构,指针的引用类型应为 unmanaged_type。 Unmanaged_type 是 reference_type 或构造类型之外的任何类型,并且在任何嵌套级别都不包含 reference_type 或构造类型字段。 换句话说, unmanaged_type 是以下项之一: sbyte、 byte 、 short 、 ushort 、 int 、 uint 、 long 、 ulong char float double decimal 、、、、或 bool ; 任何 enum_type; 任何 pointer_type; 任何不是构造类型并且只包含 unmanaged_type 的字段的用户定义的 struct_type ; 混合使用指针和引用的直观规则是允许引用 (对象的引用) 允许包含指针,但是指针的引用不允许包含引用。
固定变量地址
指定关键字 fixed
用途
防止垃圾回收器重新定位堆上的可移动变量,便于声明定义指向该变量的指针
fixed(类型* 指针 = 表达式) 语句
例:
unsafe { string str = "hello"; fixed (char* cPtr = str) //相当于 cPtr = &str[0] { for (int i = 0; ; i++) { char c = *(cPtr + i); if (c == '\0') break; Console.WriteLine(c); } } }
固定大小的缓冲区(即在结构体中声明的固定长度的数组字段)
例:
unsafe struct A { public fixed int x[5]; public fixed int y[5], z[5]; }
变量类型所占用的字节数
使用 sizeof(类型) 获取
在栈上分配内存
关键字 stackalloc
分配的内存在栈上,GC 不会回收。 使用分配 stackalloc 的内存无法显式释放。 在函数成员执行过程中创建的所有堆栈分配的内存块将在该函数成员返回时自动被丢弃。 这对应于 alloca 函数,这是一个在 C 和 c + + 实现中常见的扩展。
例:
unsafe { int* p = stackalloc int[5]; }
类
Object 类
是所有类型的基类
方法
判断指定对象是否等于当前对象
bool Equals (object obj)
判断是否为同一实例
static bool ReferenceEquals (object objA, object objB)
获取哈希值
int GetHashCode()
获取类型信息
Type GetType()
获取对象的字符串
string ToString()
浅拷贝
object MemberwiseClone()
复制对象的非静态字段; 值类型逐位复制; 引用类型只复制引用(值),不复制对象;
最终化,对象析构前调用的方法
Finalize()
C# 编译器不允许您重写 Finalize 方法。相反,您通过为您的类实现析构函数来提供终结器。因为垃圾收集是不确定的,所以您无法准确知道垃圾收集器何时执行终结。 要立即释放资源,您还可以选择实现 Dispose 方法 和 IDisposable 接口。您的类的使用者可以调用 IDisposable.Dispose 实现以释放非托管资源,如果未调用 Dispose 方法,您可以使用 Finalize 方法释放非托管资源。
匿名类型
创建
new 后面没有指定类型,直接定义了对象实体及其属性
例:
var person = new { Name = "名称", Age = 12 };
成员
字段
方法
可变参数方法
参数类型前加 params
例:
static int Add(params int[] values) //一维数组类型前加 params { int sum = 0; foreach (var item in values) sum += item; return sum; } Add(1, 2, 3);
扩展方法
静态方法中,第1个参数类型为需要扩展的类型,且类型前加 this
例:
public static class DoubleExtension { public static double Round(this double input, int digits) { return Math.Round(input, digits); } } double a = 3.14159.Round(2);
属性
例:
class Student { public int Id { get; set; } private string _name; public string Name { get { return _name; } private set { _name = value; } } public string TypeName => "Student"; //使用“表达式体成员”定义的只读属性;该方式还可以用于只读索引器、方法 }
索引器
例:
class Student { private Dictionary<string, int> scoreDic = new Dictionary<string, int>(); public int? this[string subject] { get { if (scoreDic.TryGetValue(subject, out var score)) return score; return null; } set { if (scoreDic.ContainsKey(subject)) scoreDic[subject] = value.Value; else scoreDic.Add(subject, value.Value); } } //索引器中调用另一个索引器的方法: // this[参数] } Student student = new Student(); student["A"] = 1;
事件
构造方法
静态构造方法
不能有参数、修饰符,只能有1个静态构造方法
继承
特点
派生类只能继承一个基类
使用
类中
基类使用 virtual 关键字声明虚成员
派生类中
使用 override 关键字重写基类的虚成员
若不用 override 修饰,则默认表示 new
派生类使用 new 关键字新增与基类中同名的成员
sealed 关键字
用于类时,该类无法被继承
sealed override 用于重写的成员时,该成员无法再被子类重写
构造方法中
使用 base 关键字访问 直接基类的 构造方法
使用 this 关键字访问 类的 其他构造方法
方法中
使用 base 关键字可以访问基类的成员
例:
class Vehicle { public Vehicle(double price) { this.Price = price; Name = nameof(Vehicle); } public double Price { get; set; } public virtual string Name { get; set; } //声明虚属性 } class Car : Vehicle { public Car(double price) : base(price - 10) //使用 base 关键字访问直接基类的构造方法 { this.Price = price; Name = nameof(Car); } public Car(double price, string name) : this(price) //使用 this 关键字访问类的其他构造方法 { Name = name; } public new double Price { get; set; } //使用 new 关键字新增与基类中同名的属性/方法 public override string Name { get; set; } public void ShowPrice() { Console.WriteLine(base.Price); //base 关键字可以访问基类的成员 Console.WriteLine(this.Price); } }
抽象类与接口
主要用于定义“规约”、或实现动态绑定(多态)。
抽象类
特点
是未完全实现逻辑的类
封装确定的,开放不确定的
使用
使用 abstract 关键字修饰 类或成员
接口
特点
完全未实现逻辑的类
接口实现类可以实现多个接口(可以“多重继承”)
只有方法、属性、索引器、事件成员
是一种“规约”
可以被类、结构实现
使用
接口成员默隐含都是 public 的。不过子类实现时,需要再加 public
显式接口
使用
实现时
方法前加接口名作用域,显式指明为IWindow接口的实现
显式实现的接口方法访问权限是隐含 private 的
访问时
将类实例转换为接口实例后,才能访问显式实现的接口
例:
using System; using System.Collections.Generic; using System.Collections; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleApp1 { internal class Program { static void Main(string[] args) { FileViewer viewer = new FileViewer(); IWindow window = new FileViewer(); (viewer as IWindow).Close(); //将类实例转换为接口实例后,才能访问显式实现的接口 window.Close(); //通过接口实例访问显式实现的接口 } } interface IWindow { void Close(); } interface IFileHandler { void Close(); } class FileViewer : IWindow, IFileHandler { void IWindow.Close() //方法前加接口名作用域,显式指明为IWindow接口的实现,显式实现的接口方法访问权限是隐含private的 { } void IFileHandler.Close() { } } }
例:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleApp1 { internal class Program { static void Main(string[] args) { List<IVehicle> vehicles = new List<IVehicle>() { new Car(), new Truck(), new RaceCar() }; foreach (var veh in vehicles) { veh.Fill(); veh.Run(); veh.Stop(); } new RaceCar().Music(); } } interface IVehicle //接口 { void Fill(); //接口成员默隐含都是 public 的。不过子类实现时,需要再加 public void Run(); void Stop(); } abstract class Vehicle : IVehicle //抽象类 { public void Fill() //实现接口的方法 { Console.WriteLine("加油..."); } public abstract void Run(); //接口方法实现为虚方法,具体实现延迟到子类实现 public void Stop() { Console.WriteLine("停止!"); } } class Car : Vehicle { public override void Run() //重写基类的方法。若不用 override 修饰,则默认表示 new { Console.WriteLine($"{nameof(Car)} 开动..."); } public virtual void Music() //声明虚方法。用 virtual 修饰 { Console.WriteLine($"{nameof(Car)} 播放音乐..."); } } sealed class Truck : Vehicle //sealed 用于类时,该类无法被继承 { public override void Run() { Console.WriteLine($"{nameof(Truck)} 开动..."); } } class RaceCar : Car { public sealed override void Run() //sealed override 用于方法时,该方法无法再被子类重写 { Console.WriteLine($"{nameof(RaceCar)} 开动..."); } public override void Music() //重写虚方法。属性、索引器也可以类似的方法重写 { Console.WriteLine($"{nameof(RaceCar)} 播放音乐..."); } } }
依赖倒置/依赖反转原则
就是类图里的依赖方向反转,从一个类依赖另一个类到一个类依赖接口。
特点
松耦合
可以更容易替换功能的提供方
应用
单元测试,是该原则在开发中的直接应用
嵌套类型
是在类中定义的类型,是类的一种组织方式
可以是类、结构、接口、委托等
外部访问方式
外部类名.内部类名
操作符
类型转换
操作符重载
转换操作符重载
例:
namespace ConsoleApp { internal class Program { static void Main(string[] args) { Stone stone = new Stone() { age = 5000 }; Monke monke = stone; //隐式转换 } } class Stone { public int age; //public static explicit operator Monke(Stone stone) //显式类型转换操作符重载。要转成的目标类型在操作符位置,源类型在参数位置 //{ // return new Monke { age = stone.age / 500 }; //} public static implicit operator Monke(Stone stone) //隐式类型转换操作符重载 { return new Monke { age = stone.age / 500 }; } } class Monke { public int age; } }
is
判断一个对象是否为某个类或子类的实例,或是否为某个值
支持值类型。
as
转换一个对象为某个类或基类的实例
转换失败不会抛出异常,只返回 null。 不支持值类型。
例:
if (veh is IVehicle) (veh as IVehicle).Fill(); 或 if (veh is IVehicle v) v.Fill();
基础类
基础类库
System
基本数据类型、基本数学函数、字符串处理、异常处理等
System.Collections
集合
System.Text
文字字符
System.IO
输入/输出
System.Windows.Forms
Windows 窗体
System.Drawing
基本图形操作
System.Web
Internet 相关
ASP .NET
System.Xml
处理 XML
System.Web.Services
处理基于 XML 的 Web Service
System.Data
数据、数据库编程
ADO .NET
System.Net
网络通信
System.Threading
多线程
常用运算
转换
例:
类型自带转换
double a = double.Parse("6.5");
double.TryParse("6.5",out a);
Convert 转换
命名空间: System
a = System.Convert.ToDouble("6.5");
a = (double)Convert.ChangeType("6.5", typeof(double));
Base64字符串、字节数组转换
ToBase64String、FromBase64String 方法
数学运算
Math 类
double a = Math.Round(6.5, 0, MidpointRounding.AwayFromZero); //四舍五入
随机数
例:
Random rnd = new Random();
a = rnd.Next();
a = rnd.Next(0, 100);
a = rnd.NextDouble();
时间
DateTime 结构体
System
表示时刻
TimeSpan 结构体
System
表示时间间隔
Calendar 类
System.Globalization
表示日历
主要派生类
GregorianCalendar
表示公历
ChineseLunisolarCalendar
继承 Object Calendar EastAsianLunisolarCalendar ChineseLunisolarCalendar
表示中国农历
公历转农历方法
例:
ChineseLunisolarCalendar cnlc = new ChineseLunisolarCalendar(); //农历日历 DateTime dateTime= DateTime.Now; int year = cnlc.GetYear(dateTime); //获取时刻对应的农历年 int month = cnlc.GetMonth(dateTime); //获取时刻对应的农历月 int day = cnlc.GetDayOfMonth(dateTime); //获取时刻对应的农历日
ThaiBuddhistCalendar
表示泰国佛教日历
经验
获取系统时区
System.TimeZoneInfo.GetSystemTimeZones()
转换时间为指定Id时区的时间
例:
需要 国家/地区的系统时区标识符; 获取当前Id: System.TimeZoneInfo.Local.Id; 根据时区标识符将时间转换为另一时区中的时间: System.TimeZoneInfo public static DateTime ConvertTimeBySystemTimeZoneId(DateTime dateTime, string destinationTimeZoneId);
获取月份缩写
DateTime.Now.ToString("MMM",CultureInfo.GetCultureInfo("CN"))
控制台
Console 类
表示控制台应用程序的标准输入流、输出流和错误流
主要成员
方法
void SetIn(TextReader newIn)
void SetOut(TextWriter newOut)
void SetError(TextWriter newError)
例:
cmd
Process 类
通过 Process 使用 cmd.exe 连续执行命令
using System; using System.Diagnostics; namespace ConsoleApp1 { internal class Program { static void Main(string[] args) { using (Process process = new Process()) { process.StartInfo.FileName = "cmd.exe"; process.StartInfo.CreateNoWindow = true; process.StartInfo.UseShellExecute = false; process.StartInfo.RedirectStandardInput = true; process.StartInfo.RedirectStandardOutput = true; process.StartInfo.RedirectStandardError = true; process.Start(); process.StandardInput.AutoFlush = true; string command = "dir"; string suffix = Guid.NewGuid().ToString(); //执行完后,echo输出的标定后缀,用于判断命令是否执行完毕 string reply = null; for (int i = 0; i < 3; i++) { process.StandardInput.WriteLine($"{command} && echo {suffix}"); do { reply = process.StandardOutput.ReadLine(); //使用 ReadLine 方法 } while (reply != suffix); } process.StandardInput.Close(); //关闭输入流,防止后续阻塞 string error = process.StandardError.ReadToEnd(); process.WaitForExit(5000); process.Close(); } } } }
其他
Environment 类
主要成员
属性
CommandLine 获取该进程的命令行 CurrentDirectory 获取和设置当前目录(即该进程从中启动的目录) ExitCode 获取或设置进程的退出代码 HasShutdownStarted 指示公共语言运行库是否正在关闭 MachineName 获取此本地计算机的NetBIOS名称 NewLine 获取为此环境定义的换行字符串 OSVersion 获取包含当前平台标识符和版本号的OperatingSystem对象 StackTrace 获取当前的堆栈跟踪信息 SystemDirectory 获取系统目录的完全限定路径 TickCount 获取系统启动后经过的毫秒数 UserDomainName 获取与当前用户关联的网络域名 UserInteractive 获取一个值,用以指示当前进程是否在用户交互模式中运行 UserName 获取启动当前线程的用户名 Version 获取一个Version对象,该对象描述公共语言运行库的主版本、次版本、内部版本和修订号 WorkingSet 获取映射到进程上下文的物理内存量 Is64BitOperatingSystem 是否为 64 位操作系统 Is64BitProcess 是否为 64 位进程
方法
Exit 终止此进程并为基础操作系统提供指定的退出代码 ExpandEnvironmentVariables 将嵌入到指定字符串中的每个环境变量的名称替换为该变量的值的等效字符串,然后返回结果字符串("%环境变量名%") GetCommandLineArgs 返回包含当前进程的命令行参数的字符串数组 GetEnvironmentVariable 返回指定环境变量的值 GetEnvironmentVariables 返回所有环境变量及变量值(返回 IDictionary 类型,用 DictionaryEntry 遍历项) GetFolderPath 获取指向由指定枚举标识的系统特殊文件夹的路径 GetLogicalDrives 返回逻辑驱动器名称的字符串数组
获取系统版本
使用 Environment.OSVersion.Version
var version = System.Environment.OSVersion.Version; 判断XP: version.Major < 6 系统版本: Windows 11 10.0* Windows 10 10.0* Windows Server 2022 10.0* Windows Server 2019 10.0* Windows Server 2016 10.0* Windows 8.1 6.3* Windows Server 2012 R2 6.3* Windows 8 6.2 Windows Server 2012 6.2 Windows 7 6.1 Windows Server 2008 R2 6.1 Windows Server 2008 6.0 Windows Vista 6.0 Windows Server 2003 R2 5.2 Windows Server 2003 5.2 Windows XP 64 位版本 5.2 Windows XP 5.1 Windows 2000 5.0
枚举
基类
Enum
主要方法
GetName(~)
GetNames(~)
GetValues(~)
声明
例:
enum Color : int { Red, Green = 1, Blue }
访问
需通过“类型.元素”访问成员
转换值时用强制转换
转换
整数转换
int b = (int)Color.Blue; Color r = (Color)0;
字符串转换
string rs = r.ToString(); r = (Color)Enum.Parse(typeof(Color), rs);
标志特性
FlagsAttribute
指示可将枚举视为位域(即一组标志)
主要影响 ToString() 方法。
例:
[Flags] public enum ExecutionState : UInt32 { SYSTEM_REQUIRED = 0x00000001u, DISPLAY_REQUIRED = 0x00000002u, }
数组
基类
System.Array
属性
Rank
维数
方法
GetLength (int dimension)
获取指定维数的长度
Sort<T>(T[] array, Comparison<T> comparison)
排序数组元素
例:
文件名称按数值大小排序
public class Shlwapi { [DllImport("shlwapi.dll", CharSet = CharSet.Unicode, EntryPoint = "StrCmpLogicalW")] public static extern int StrCmpLogicalW(string psz1, string psz2); //支持 XP } 返回值: 如果字符串相同,则返回零; 如果 psz1 指向的字符串的值大于 psz2 指向的值,则返回 1; 如果 psz1 指向的字符串的值小于 psz2 指向的值,则返回 -1。 var files = directoryInfo.GetFiles(); Array.Sort(files, (a, b) => Shlwapi.StrCmpLogicalW(a.FullName, b.FullName));
声明定义
一维数组
type[] arrayName = new type[arraySize]
二维数组
double[,] f = new double[3, 4]
多维数组
double[,,] f = new double[3, 4, 5]
交错数组
表示数组的数组
int[][] nums = new int[2][] { new int[] { 1, 2, 3 }, new int[] { 4, 5, 6 } };
例:
获取一个数组指定偏移位置后的数据
public static byte[] GetOffsetArray(byte[] buffer, int offset) { byte[] result = new byte[buffer.Length - offset]; Array.Copy(buffer, offset, result, 0, result.Length); return result; }
泛型
泛型方法
例:
using System; namespace ConsoleApp2 { internal class Program { static void Main(string[] args) { //Calc.Show<int>(0); //参数类型是值类型,不符合约束 Calc.Show<Type>(typeof(Calc)); //<Type> 指明泛型类型实参 } } public static class Calc { public static void Show<T>(T a) where T : class //泛型方法,T为泛型类型参数,并添加了引用类型约束 { Console.WriteLine(a); } } }
泛型类
例:
using System; namespace ConsoleApp2 { internal class Program { static void Main(string[] args) { var num = new Number<int> { Value = 123 }; num.Show(); } } public class Number<T> where T : struct //泛型类 { public T Value { get; set; } = default; public void Show() { Console.WriteLine(Value); } } class Dictionary<TKey, TVal> where TKey : IComparable, IEnumerable //单个约束的多个条件间用“,”分隔 //多个约束间用 空白字符 分隔 where TVal : IComparable { public void Add(TKey key, TVal val) { } } }
类型参数的约束
where T : struct
值类型
where T : class
引用类型
where T : new()
包含 public 无参构造方法的类型(有多个约束时,该约束放最后)
where T : 类型名
指定类型或派生自指定类型的类型
where T : 接口名, 接口名, ...接口名
指定的接口或实现了指定接口的类型(可指定多个)
多个的分隔方法
单个约束的多个条件间用“,”分隔
多个约束间用 空白字符 分隔
泛型接口或泛型委托的协变与逆变
说明
协变
泛型参数类型变化的方向,与隐式转换中子类→父类实例的方向相同
逆变
泛型参数类型变化的方向,与隐式转换中子类→父类实例的方向相反
输出的协变性
(生产者)方法要求输出父类,内部返回子类。签名参数类型由子类→父类,实例类型由子类→父类
可以参考Java中的“Producer Extends”
输入的逆变性
(消费者)方法要求输入父类,外部传入子类。签名参数类型由父类→子类,实例类型仍由子类→父类
可以参考Java中的“Consumer Super”
例:
using System; namespace ConsoleApp1 { interface ICov<T0, in T1, out T2> //in:逆变 out:协变。 //让编译器分清转换发生的阶段,从而判断是否允许转换 { T0 GetT0(T0 t0); T2 GetT2(T1 t1); } public class Cov : ICov<string, object, string> { public string GetT0(string t0) { return t0.ToString(); } public string GetT2(object t1) { return t1.ToString(); } } public delegate TResult DFunc<T, TResult>(T t); internal class Program { static void Main(string[] args) { ICov<string, object, string> cov1 = new Cov(); ICov<string, string, object> cov2 = cov1; //接口的T1类型(有in修饰)允许由object逆变为string, //T2类型(有out修饰)允许由string协变为object, //协变逆变增强了接口的适用性。而T0是不变的,无法允许这样的变化 Func<object, string> func1 = cov1.GetT2; Func<string, object> func2 = func1; //Func 委托的T类型(有in修饰)允许由object逆变为string, //TResult类型(有out修饰)允许由string协变为object //DFunc<object, string> dFunc1 = cov1.GetT2; //DFunc<string, object> dFunc2 = dFunc1; //DFunc 委托的T类型(无in修饰)不允许由object逆变为string, // //TResult类型(无out修饰)不允许由string协变为object } } }
应用
元组
System.Tuple 多元组类(引用类型)
例:
创建
Tuple<double, int> dot = new Tuple<double, int>(1d, 2);
System.ValueTuple 元组类(值类型)
例:
创建
方式1
ValueTuple<double, int> vdot = new ValueTuple<double, int>(1d, 2);
方式2
(double, int) vb = (1d, 2);
通过 Item1、Item2 等访问分量
Console.WriteLine($"{vb.Item1}");
给分量取名字
(double price, int num) vc = (1d, 2);
通过分量名访问
Console.WriteLine($"{vc.price}");
解构分量
(double price, int num) = vc;
即直接定义多个变量(形式上类似给分量取名,只是没有元组变量)。
访问解构后的变量
Console.WriteLine($"{price}");
(double price, _) = vc;
解构分量,不需要的分量可以使用 _ 弃元
集合
命名空间
System.Collections
定义各种对象集合的接口和类
System.Collections.Generic
定义泛型集合的接口和类
System.Collections.Specialized
专用强类型集合
常用集合类
List<T>
动态数组、链表
Stack<T>
栈
Queue<T>
队列
Dictionary<TKey, TValue>
字典
Hashtable
哈希表
根据哈希代码组织的键值对
概念
哈希关键字
hash key、哈希值
是一个能根据数据项快速计算出来的整数值
若两个或以上表项具有相同的哈希关键字,则 Hashtable 中的桶将包含多个值。 这时,还需要比较桶中每项的关键字,以便找到数据项
哈希表
hash table
包含一组数据桶
每个桶能够包含与某个哈希关键字相关的数据
桶中的项数比例称为哈希表的负载因子
负载因子越小,检索速度越快,存储空间开销越大。默认1.0
SortedList<TKey,TValue>
基于相关的 IComparer<T> 实现按键进行排序的键/值对的集合
SortedDictionary<TKey,TValue>
根据键进行排序的键/值对的集合
HashSet<T>
值的集
任意 2 个元素不相等,如果存放 2 个相等的元素,则原来的元素会被替换
SortedSet<T>
有顺序的值的集
BitArray
管理位值的压缩数组,这些值以布尔值的(数组)形式表示
例:
BitArray bitArray = new BitArray(new int[5] { 6, 7, 8, 9, 10 });
每个位的状态表示为数组的一个布尔元素
StringCollection
字符串集合
StringDictionary
字符串字典
NameValueCollection
通过键或索引访问的关联 String 键和 String 值的集合
迭代
可枚举/迭代类
实现 System.Collections.IEnumerable 接口
成员
方法
IEnumerator GetEnumerator ()
枚举器
实现 System.Collections.IEnumerator 接口
成员
属性
object Current { get; }
方法
bool MoveNext ()
void Reset ()
例
不用 yield 语句
using System; using System.Collections.Generic; using System.Collections; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleApp1 { internal class Program { static void Main(string[] args) { int[] nums1 = { 1, 2, 3, 4, 5, 6, 7, 8 }; ReadOnlyCollection readOnlyCollection = new ReadOnlyCollection(nums1); Console.WriteLine(Sum(readOnlyCollection)); } static int Sum(IEnumerable nums) { int sum = 0; foreach (int n in nums) sum += (int)n; return sum; } } class ReadOnlyCollection : IEnumerable { private int[] _array; public ReadOnlyCollection(int[] array) { _array = array; } public IEnumerator GetEnumerator() { return new Enumerator(this); } public class Enumerator : IEnumerator //枚举器一般用实现用内部类实现,便于访问成员 { private ReadOnlyCollection _collection; private int _index; public Enumerator(ReadOnlyCollection collection) { _collection = collection; _index = -1; } public object Current { get { object cur = _collection._array[_index]; return cur; } } public bool MoveNext() { return ++_index < _collection._array.Length; } public void Reset() { _index = -1; } } } }
用 yield 语句
using System; using System.Collections.Generic; using System.Collections; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleApp1 { internal class Program { static void Main(string[] args) { int[] nums1 = { 1, 2, 3, 4, 5, 6, 7, 8 }; ReadOnlyCollection readOnlyCollection = new ReadOnlyCollection(nums1); Console.WriteLine(Sum(readOnlyCollection)); } static int Sum(IEnumerable nums) { int sum = 0; foreach (int n in nums) sum += (int)n; return sum; } } class ReadOnlyCollection : IEnumerable { private int[] _array; public ReadOnlyCollection(int[] array) { _array = array; } public IEnumerator GetEnumerator() { for (int i = 0; i < _array.Length; i++) { //if (i > 5) // yield break; //中途结束枚举 yield return _array[i]; //返回当前枚举结果 } } } }
Enumerable 类
方法
元素类型强制转换
static IEnumerable<TResult> Cast<TResult>(this IEnumerable source)
TResult:source 中的元素要强制转换成的类型
根据指定类型筛选元素
static IEnumerable<TResult> OfType<TResult> (this IEnumerable source)
根据相等比较器确定两个序列是否相等
static bool SequenceEqual<TSource> (this IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer);
//例: public class StringIgnoreCaseComparer : IEqualityComparer<string> { public bool Equals(string x, string y) { if (x == null && y == null) return true; else if (x == null || y == null) return false; else return x.Equals(y, StringComparison.OrdinalIgnoreCase); } public int GetHashCode(string obj) { if (obj == null) return 0; return obj.GetHashCode(); } }
排序
比较
IComparable
定义由值类型或类实现的特定于类型的通用比较方法
成员
方法
int CompareTo (object obj)
IComparer
提供比较两个对象的方法
成员
方法
int Compare (object x, object y)
Comparison<T>
比较同一类型的两个对象的方法
类型
delegate int Comparison<in T>(T x, T y)
相等
比较
IEqualityComparer<in T>
定义用于支持比较对象是否相等的方法
成员
方法
bool Equals(T x, T y)
Linq
概念
语言集成查询(Language Integrated Query)
用途
Linq to object、Linq to database、Linq to XML
查询写法
查询表达式语法
类似 SQL 语句
例:
using System.Linq; namespace ConsoleApp1 { internal class Program { static void Main(string[] args) { string[] languages = { "Java", "C#", "C++", "Delphi", "VB.net", "VC.net", "Perl", "Python" }; var query = from item in languages //from 笛卡尔积放前面,方便编译器推断类型 where item.Length > 2 group item by item.Length into lengthGroups //group by into 返回 IGrouping<TKey,TElement> 类型的临时数据 orderby lengthGroups.Key ascending select lengthGroups; } } }
查询方法语法
类似方法链式调用
例:
投影操作符: Select、SelectMany 限制操作符: Where 排序操作符: OrderBy、OrderByDescending、ThenBy、ThenByDescending、Reverse 联接操作符: Join、GroupJoin 分组操作符: GroupBy 串联操作符: Concat 聚合操作符: Aggregate、Average、Count、LongCount、Max、Min、Sum 集合操作符: Distinct、Union、Intersect、Except 生成操作符: Empty、Range、Repeat 转换操作符: AsEnumerable、Cast、OfType、ToArray、ToDictionary、ToList、ToLookup 元素操作符: DefaultIfEmpty、ElementAt、ElementAtOrDefault、First、Last、FirstOrDefault、LastOrDefault、Single、SingleOrDefault 相等操作符: SequenceEqual 量词操作符: All、Any、Contains 分割操作符: Skip、SkipWhile、Take、TakeWhile
筛选出对象集合中,某个字段重复的对象
//示例类: public class Student { public string Name { get; set; } public int Class { get; set; } } //步骤: List<Student> students = new List<Student>() { new Student() { Name = "aaa", Class = 1 }, new Student() { Name = "aaa", Class = 2 }, new Student() { Name = "aba", Class = 3 }, new Student() { Name = "aaa", Class = 4 }, new Student() { Name = "aba", Class = 5 }, new Student() { Name = "aba", Class = 6 }, new Student() { Name = "aaa", Class = 7 }, new Student() { Name = "caa", Class = 8 }, new Student() { Name = "caa", Class = 9 }, new Student() { Name = "aaa", Class = 10 }, new Student() { Name = "aad", Class = 11 }, new Student() { Name = "aae", Class = 12 }, }; List<IGrouping<string, Student>> duplGroups = students.GroupBy(x => x.Name).Where(g => g.Count() > 1).Select(g => g).ToList(); //每个分组下的元素数大于1,表示有重复的对象 List<string> duplNames = duplGroups.Select(g => g.Key).ToList(); //Key 属性是每个组分组所依据的键,这里即为 Name 属性
字符串
(不可变)字符串
String
System
可变字符串
StringBuilder
System.Text
格式化字符串
格式字符串
{N[,M][:格式字符[精度]]}
N
参数索引号(从 0 起始)
M
宽度
为(+)数时
右对齐
为 – 数时
左对齐
格式字符
C currency 货币
D decimal 整数
E exponent 科学计数
F fixed-point 定点数
G general 通用数字
N number 数目
P percent 百分数
R round-trip
X hexadecimal
精度
例:
F2
形象描述字符
0
例: 000
无数字时输出0占位符
#
例: ###
只输出有效位
.
例: 0.00
十进制输出时显示 .
,
例: #,#
输出千位分隔
%
例: #.00%
输出百分数
自定义日期时间
yyyy 年
MM 月
dd 日
HH 时
mm 分
ss 秒
fff 千分之几秒(毫秒)
拼接
string s1 = 000.ToString() + 111.ToString();
等价于 s1 = string.Format("{0}{1}", 000, 111); //{0}、{1} 预先占位
等价于 s1 = $"{000}{111}"; //$开头的字符串嵌入,C#6.0开始支持
取消转义
@开头的字符串
@"aaa ""bbb"" c\cc"
等价于: "aaa \"bbb\" c\\cc"
忽略大小写比较
bool flag = "a".Equals("A", StringComparison.OrdinalIgnoreCase);
空格处理
删除
Trim()、TrimStart()、TrimEnd()
重载方法
删除数组中指定的一组字符的 所有前导匹配项和尾随匹配项
Trim(params char[])、TrimStart(params char[])、TrimEnd(params char[])
填充
PadLeft(~)、PadRight(~)
字符串的暂留
常量字符串是暂留的
例:
var str1 = "abc" + "123"; var str2 = "abc" + "123"; bool isEqual = str1 == str2; //true isEqual = object.ReferenceEquals(str1, str2); //true
动态创建的字符串不是暂留的
手动加入暂存池的方法
static string Intern (string str)
若字符串在暂存池中已存在,则返回暂存池中的引用; 若字符串在暂存池中不存在,则存入暂存池并返回暂存池中的引用;
例:
var str1 = $"abc{123}"; var str2 = $"abc{123}"; bool isEqual = str1 == str2; //true isEqual = object.ReferenceEquals(str1, str2); //false。lock 语句中也会用到 object.ReferenceEquals 方法,lock 字符串时注意使用 string.Intern 方法 isEqual = object.ReferenceEquals(string.Intern(str1), string.Intern(str2)); //true
经验
各单词首字母大写
System.Globalization.CultureInfo.CurrentCulture.TextInfo.ToTitleCase(str);
正则表达式
类
命名空间:System.Text.RegularExpressions
Regex
创建对象
string pattern = @"(\w*)=(\w*)"; //正则表达式模式 Regex regex = new Regex(pattern);
是否匹配
string str = ";dsf;sdfdsfd=;lkdj=iejwoi;lsjsdk=lksdjlds;kjsdlfds=;=l;sdfsdffddsf"; bool ok = regex.IsMatch(str);
ok = Regex.IsMatch(str, pattern);
获取匹配对象
单个
Match mc = regex.Match(str);
mc = Regex.Match(str, pattern);
多个
MatchCollection mcs = regex.Matches(str); foreach (Match item in mcs) { string name = item.Result("$1"); string value = item.Result("$2"); }
mcs = Regex.Matches(str, pattern);
查找
string pattern2 = @"(?<name>\w*)([^=]{0})((=){1})([^=]{0})(?<value>(\w|=|\+)*)"; //使用 ?<> 定义匹配的组名 Match mc2 = Regex.Match(str, pattern2); var resStr = mc2.Result("键:${name},值:${value}"); //通过组名查找
等价于:
resStr = $"键:{mc2.Groups["name"].Value},值:{mc2.Groups["value"].Value}";
关于 Result 方法的参数 replacement 替换模式中的替换元素
$&
包括替换字符串中整个匹配项的副本
$数字
包括替换字符串中的由 数字 标识的捕获组所匹配的最后一个子字符串
即第“数字”个捕获组。
${组名}
包括替换字符串中由 (?<组名>) 指定的组名所匹配的最后一个子字符串
$+
包括在替换字符串中捕获的最后一个组
$$
包括替换字符串中的单个“$”文本
$_
包括替换字符串中的整个输入字符串
$`
包括替换字符串中的匹配项前的输入字符串的所有文本
$'
包括替换字符串中的匹配项后的输入字符串的所有文本
例:手机号脱敏
public static string PhoneNumberMask(string number, char mask = '*') { try { if (number == null) return number; StringBuilder stringBuilder = new StringBuilder(); string pattern11 = @"(?<head>\d{3})(?<mask>\d{4})(?<tail>\d{4})"; //11位数字串 if (Regex.IsMatch(number, pattern11)) { Match mc = Regex.Match(number, pattern11); stringBuilder.Append(mc.Result("${head}")); mc.Result("${mask}").ToList().ForEach(c => stringBuilder.Append(mask)); stringBuilder.Append(mc.Result("${tail}")); } else { stringBuilder.Append(number); } return stringBuilder.ToString(); } catch (Exception ex) { return number; } }
替换
resStr = Regex.Replace(str, pattern2, "键:${name},值:${value}");
正则表达式
模式
位置
^
从行首向后匹配
$
从行尾向前匹配
\b
匹配一个单词的边界
例: abc def \bab ef\b
\B
匹配非单词的边界
字符
.
匹配除回车换行之外的任何字符
等价于
[^\r\n]
[]
匹配字符集中的字符
例: [abc] [a-c] [a-cd-f]
[^]
匹配除了字符集中的字符
例: [^a-cd-f]
()
表示一组
例: (abc|bcd|cde) 表示匹配abc、bcd、cde三者之一
|
表示或逻辑
\w
匹配字母、数字、下划线
等价于
[A-Za-z0-9_]
\W
匹配非字母、数字、下划线
等价于
[^A-Za-z0-9_]
\d
匹配数字
\D
匹配非数字
\s
匹配空白字符
\S
匹配非空白字符
\
转义
用于匹配一些特殊字符
例: \$ \( \)
次数
?
匹配前面的子表达式零次或一次
例如,"do(es)?" 可以匹配 "do" 、 "does" 中的 "does" 、 "doxy" 中的 "do"
等价于
{0,1}
放在其他“次数”字符后面时,表示尽可能少的匹配
例: 字符串: He said--decisively--that the time--whatever time it was--had come. --(.+?)-- 匹配(匹配出的字符更少) --decisively-- --whatever time it was-- --(.+)-- 匹配(匹配出的字符更多) --decisively--that the time--whatever time it was--
还可以为被组匹配的字符串加组名
例:(?<组名>)
+
匹配前面的子表达式一次或多次
例如,'zo+' 能匹配 "zo" 以及 "zoo",但不能匹配 "z"
等价于
{1,}
*
匹配前面的子表达式零次或多次
例如,zo* 能匹配 "z" 以及 "zoo"
等价于
{0,}
{n}
匹配确定的 n 次
例如,'o{2}' 不能匹配 "Bob" 中的 'o',但是能匹配 "food" 中的两个 o
{n,}
至少匹配 n 次
例如,'o{2,}' 不能匹配 "Bob" 中的 'o',但能匹配 "foooood" 中的所有 o
{n,m}
最少匹配 n 次且最多匹配 m 次
例如,"o{1,3}" 将匹配 "fooooood" 中的前三个 o
模式修饰
(?i)
不区分大小写
测试网站
https://regexr.com/
常用表达式
一、校验数字的表达式 数字:^[0-9]*$ n位的数字:^\d{n}$ 至少n位的数字:^\d{n,}$ m-n位的数字:^\d{m,n}$ 零和非零开头的数字:^(0|[1-9][0-9]*)$ 非零开头的最多带两位小数的数字:^([1-9][0-9]*)+(\.[0-9]{1,2})?$ 带1-2位小数的正数或负数:^(\-)?\d+(\.\d{1,2})$ 正数、负数、和小数:^(\-|\+)?\d+(\.\d+)?$ 有两位小数的正实数:^[0-9]+(\.[0-9]{2})?$ 有1~3位小数的正实数:^[0-9]+(\.[0-9]{1,3})?$ 非零的正整数:^[1-9]\d*$ 或 ^([1-9][0-9]*){1,3}$ 或 ^\+?[1-9][0-9]*$ 非零的负整数:^\-[1-9][]0-9"*$ 或 ^-[1-9]\d*$ 非负整数:^\d+$ 或 ^[1-9]\d*|0$ 非正整数:^-[1-9]\d*|0$ 或 ^((-\d+)|(0+))$ 非负浮点数:^\d+(\.\d+)?$ 或 ^[1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0$ 非正浮点数:^((-\d+(\.\d+)?)|(0+(\.0+)?))$ 或 ^(-([1-9]\d*\.\d*|0\.\d*[1-9]\d*))|0?\.0+|0$ 正浮点数:^[1-9]\d*\.\d*|0\.\d*[1-9]\d*$ 或 ^(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*))$ 负浮点数:^-([1-9]\d*\.\d*|0\.\d*[1-9]\d*)$ 或 ^(-(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*)))$ 浮点数:^(-?\d+)(\.\d+)?$ 或 ^-?([1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0)$ 二、校验字符的表达式 汉字:^[\u4e00-\u9fa5]{0,}$ 英文和数字:^[A-Za-z0-9]+$ 或 ^[A-Za-z0-9]{4,40}$ 长度为3-20的所有字符:^.{3,20}$ 由26个英文字母组成的字符串:^[A-Za-z]+$ 由26个大写英文字母组成的字符串:^[A-Z]+$ 由26个小写英文字母组成的字符串:^[a-z]+$ 由数字和26个英文字母组成的字符串:^[A-Za-z0-9]+$ 由数字、26个英文字母或者下划线组成的字符串:^\w+$ 或 ^\w{3,20}$ 中文、英文、数字包括下划线:^[\u4E00-\u9FA5A-Za-z0-9_]+$ 中文、英文、数字但不包括下划线等符号:^[\u4E00-\u9FA5A-Za-z0-9]+$ 或 ^[\u4E00-\u9FA5A-Za-z0-9]{2,20}$ 可以输入含有^%&',;=?$\"等字符:[^%&',;=?$\x22]+ 禁止输入含有~的字符:[^~\x22]+ 三、特殊需求表达式 Email地址:^\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$ 域名:[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+\.? InternetURL:[a-zA-z]+://[^\s]* 或 ^http://([\w-]+\.)+[\w-]+(/[\w-./?%&=]*)?$ 手机号码:^(13[0-9]|14[5|7]|15[0|1|2|3|4|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$ 电话号码("XXX-XXXXXXX"、"XXXX-XXXXXXXX"、"XXX-XXXXXXX"、"XXX-XXXXXXXX"、"XXXXXXX"和"XXXXXXXX):^(\(\d{3,4}-)|\d{3.4}-)?\d{7,8}$ 国内电话号码(0511-4405222、021-87888822):\d{3}-\d{8}|\d{4}-\d{7} 电话号码正则表达式(支持手机号码,3-4位区号,7-8位直播号码,1-4位分机号): ((\d{11})|^((\d{7,8})|(\d{4}|\d{3})-(\d{7,8})|(\d{4}|\d{3})-(\d{7,8})-(\d{4}|\d{3}|\d{2}|\d{1})|(\d{7,8})-(\d{4}|\d{3}|\d{2}|\d{1}))$) 身份证号(15位、18位数字),最后一位是校验位,可能为数字或字符X:(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$) 帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线):^[a-zA-Z][a-zA-Z0-9_]{4,15}$ 密码(以字母开头,长度在6~18之间,只能包含字母、数字和下划线):^[a-zA-Z]\w{5,17}$ 强密码(必须包含大小写字母和数字的组合,不能使用特殊字符,长度在 8-10 之间):^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])[a-zA-Z0-9]{8,10}$ 强密码(必须包含大小写字母和数字的组合,可以使用特殊字符,长度在8-10之间):^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,10}$ 日期格式:^\d{4}-\d{1,2}-\d{1,2} 一年的12个月(01~09和1~12):^(0?[1-9]|1[0-2])$ 一个月的31天(01~09和1~31):^((0?[1-9])|((1|2)[0-9])|30|31)$ 钱的输入格式: 有四种钱的表示形式我们可以接受:"10000.00" 和 "10,000.00", 和没有 "分" 的 "10000" 和 "10,000":^[1-9][0-9]*$ 这表示任意一个不以0开头的数字,但是,这也意味着一个字符"0"不通过,所以我们采用下面的形式:^(0|[1-9][0-9]*)$ 一个0或者一个不以0开头的数字.我们还可以允许开头有一个负号:^(0|-?[1-9][0-9]*)$ 这表示一个0或者一个可能为负的开头不为0的数字.让用户以0开头好了.把负号的也去掉,因为钱总不能是负的吧。下面我们要加的是说明可能的小数部分:^[0-9]+(.[0-9]+)?$ 必须说明的是,小数点后面至少应该有1位数,所以"10."是不通过的,但是 "10" 和 "10.2" 是通过的:^[0-9]+(.[0-9]{2})?$ 这样我们规定小数点后面必须有两位,如果你认为太苛刻了,可以这样:^[0-9]+(.[0-9]{1,2})?$ 这样就允许用户只写一位小数.下面我们该考虑数字中的逗号了,我们可以这样:^[0-9]{1,3}(,[0-9]{3})*(.[0-9]{1,2})?$ 1到3个数字,后面跟着任意个 逗号+3个数字,逗号成为可选,而不是必须:^([0-9]+|[0-9]{1,3}(,[0-9]{3})*)(.[0-9]{1,2})?$ 备注:这就是最终结果了,别忘了"+"可以用"*"替代如果你觉得空字符串也可以接受的话(奇怪,为什么?)最后,别忘了在用函数时去掉去掉那个反斜杠,一般的错误都在这里 xml文件:^([a-zA-Z]+-?)+[a-zA-Z0-9]+\\.[x|X][m|M][l|L]$ 中文字符的正则表达式:[\u4e00-\u9fa5] 双字节字符:[^\x00-\xff] (包括汉字在内,可以用来计算字符串的长度(一个双字节字符长度计2,ASCII字符计1)) 空白行的正则表达式:\n\s*\r (可以用来删除空白行) HTML标记的正则表达式:<(\S*?)[^>]*>.*?|<.*? /> ( 首尾空白字符的正则表达式:^\s*|\s*$或(^\s*)|(\s*$) (可以用来删除行首行尾的空白字符(包括空格、制表符、换页符等等),非常有用的表达式) 腾讯QQ号:[1-9][0-9]{4,} (腾讯QQ号从10000开始) 中国邮政编码:[1-9]\d{5}(?!\d) (中国邮政编码为6位数字) IPv4地址:((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})(\.((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})){3}
委托与事件
委托
可以理解成C语言中的“函数指针”
使用
委托类型声明
修饰符 delegate 返回类型 委托名(参数表);
委托是一种类,与class是同一语法级别
委托变量声明
委托名 委托变量名;
委托(变量)实例化
使用构造方法
new 委托名(方法名);
方法可以是实例的方法,也可以是类的静态方法。
简写,直接使用方法名赋值
方法名;
使用匿名函数(方法)
delegate (参数表){方法体};
使用Lambda表达式
(参数表) => {方法体};
多播(合并+,移除-)
委托变量名 += 委托实例;
委托变量名 -= 委托实例;
委托(变量)调用
委托变量名(参数表);
委托变量名.Invoke(参数表); //多播
例:
namespace P31ConsoleApp2 { internal class Program { static void Main(string[] args) { DAdd add = new DAdd(Add); //2.声明非泛型委托变量,并实例化 int c = add(1, 2); //3.调用 DAdd<int> addT = new DAdd<int>(Add); //2.声明泛型委托变量,并实例化 c = addT(3, 4); //3.调用 } static int Add(int a, int b) { return a + b; } } delegate int DAdd(int a, int b); //1.声明非泛型委托类型 delegate T DAdd<T>(T a, T b); //1.声明泛型委托类型 }
多播委托
例:
static void Main(string[] args) { Action action1 = OutputRedForegroundColor; Action action2 = OutputGreenForegroundColor; Action action3 = OutputBlueForegroundColor; action1 += action2; //添加 action1 += OutputBlueForegroundColor; action1.Invoke(); //同步调用可以调用多播 action3.BeginInvoke(null, null); //异步调用只能调用单播 } static void OutputRedForegroundColor() { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine(Console.ForegroundColor); } static void OutputGreenForegroundColor() { Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine(Console.ForegroundColor); } static void OutputBlueForegroundColor() { Console.ForegroundColor = ConsoleColor.Blue; Console.WriteLine(Console.ForegroundColor); }
系统预定义委托
System.Action
表示无返回值的
System.Func
表示有返回值的
事件
可以理解成C语言中的“回调函数”
使用
(定义 事件参数类(型))
事件参数类(型)一般要继承 System.EventArgs
(声明 事件处理器方法 委托类型)
类型 与 事件处理器方法 相同
不定义也可使用泛型委托 System.EventHandler<TEventArgs>
事件源类
事件源类中,声明事件(变量)
修饰符 event 委托名 事件名;
类似委托变量声明。 事件名用动词。
事件是委托字段的包装器,保护委托字段不被滥用
不含显式访问器的事件可以在定义事件的类中调用(类似委托调用), 在类外部,事件只允许 += 和 -= 操作;
事件源类中,调用事件
一般由 protected 修饰的方法触发事件。 命名:On事件名(表示事出有因)
事件名(参数表);
委托变量名.Invoke(参数表)
该形式用于含显式访问器的事件,使用支持委托字段调用;
事件订阅类
(事件订阅类中,定义(实现)事件方法)
本质上是个回调方法。
不定义也可在事件注册时使用lambda表达式或匿名函数
将事件订阅类的事件方法,注册到事件源类的事件
类似委托(变量)实例化
事件名 += 委托实例;
事件名 -= 委托实例;
例:
using System; namespace P22Console2 { internal class Program { static void Main(string[] args) { Customer customer = new Customer(); Waiter waiter = new Waiter(); customer.Order += waiter.OrderReceive; customer.OrderAppend += waiter.OrderAppendReceive; customer.PlaceOrder(); customer.PayTheBill(); } } //1.事件参数类型 public class OrderEventArgs : EventArgs //命名:事件名EventArgs { public string DishName { get; set; } public string Size { get; set; } } //2.自定义事件处理器方法委托 public delegate void OrderEventHandler(Customer customer, OrderEventArgs e); //命名:事件名EventHandler //3.事件源 public class Customer { //方式1:使用自定义事件处理器方法委托声明事件(含访问器(若是抽象事件,则不能有)) private OrderEventHandler orderEventHandler; public event OrderEventHandler Order //命名:用动词,注意时态 { add { orderEventHandler += value; } remove { orderEventHandler -= value; } } //方式2:使用泛型事件处理器方法委托声明事件(不含显式访问器)(推荐) public event EventHandler<OrderEventArgs> OrderAppend; protected void OnOrder(string dishName, string size) //一般由 protected 修饰的方法触发事件。命名:On事件名(表示事出有因) { if (orderEventHandler != null) //Order事件有显式定义的访问器,这里只能用委托字段去判空 { OrderEventArgs e = new OrderEventArgs(); e.DishName = dishName; e.Size = size; orderEventHandler.Invoke(this, e); //调用 } } protected void OnOrderAppend(string dishName, string size) { if (OrderAppend != null) //OrderAppend事件没有显式定义的访问器,这里可以用事件字段去判空 { OrderEventArgs e = new OrderEventArgs(); e.DishName = dishName; e.Size = size; OrderAppend(this, e); //调用 } } public void PlaceOrder() { OnOrder("白菜", "大"); OnOrderAppend("花生", "小"); } public double Bill { get; set; } public void PayTheBill() { Console.WriteLine($"已支付 ${Bill}."); } } //4.事件订阅类 public class Waiter { public void OrderReceive(Customer customer, OrderEventArgs e) { Console.WriteLine($"收到订单:{e.Size} {e.DishName}"); customer.Bill += GetPrice(e.Size, false); } public void OrderAppendReceive(object sender, OrderEventArgs e) { Customer customer = sender as Customer; Console.WriteLine($"收到附加订单:{e.Size} {e.DishName}"); customer.Bill += GetPrice(e.Size, true); } private double GetPrice(string size, bool isAppend) { double price = isAppend ? 5 : 10; switch (size) { case "小": price *= 0.5; break; case "大": price *= 1.5; break; default: break; } return price; } } }
表达式树
命名空间
System.Linq.Expressions
类
Expression
例:
获取变量名字符串
public static string GetVarName<T>(System.Linq.Expressions.Expression<Func<T>> exp) //使用:GetVarName(() => 变量) { return ((System.Linq.Expressions.MemberExpression)exp.Body).Member.Name; }
文件
流
即输入/输出源的抽象
System.IO. Stream 抽象类
主要成员
属性
CanRead、CanWrite
CanSeek、Position、Length
方法
Seek、SetLength
Seek 方法,在流中对搜索指针进行定位,用来决定下一步的读/写操作的位置。
BeginRead、EndRead,BeginWrite、EndWrite
Read、Write,ReadByte、WriteByte
Flush、Close
CopyTo、CopyToAsync
主要派生类
FileStream
表示文件操作
例:
流的内容保存到文件
使用 CopyTo
//同步方法: using(FileStream fs = File.Create("file.dat")) { dataStream.Seek(0, SeekOrigin.Begin); //设置复制开始的地方 await dataStream.CopyTo(fs); } //异步方法: using(FileStream fs = File.Create("file.dat")) { dataStream.Seek(0, SeekOrigin.Begin); //设置复制开始的地方 await dataStream.CopyToAsync(fs); }
MemoryStream
表示内存操作
BufferedStream
表示缓冲处理
通过装饰器模式,实现对其他流的性能优化
ZLibStream
GZipStream
NetworkStream
System.Net.Sockets
读写操作类
BinaryReader、BinaryWriter
TextReader 抽象类
派生类
StreamReader
StringReader
TextWriter 抽象类
派生类
StreamWriter
向流中写字符
StringWriter
向字符串中写字符
IndentedTextWriter
使用缩进控制写字符
HttpWriter
向 HTTP 响应对象写字符
HtmlTextWriter
向浏览器写 HTML
例:
流中字符串的读取
using System.IO; using System.Text; namespace ConsoleApp1 { internal class Program { static void Main(string[] args) { string str = "123456789abcdefghijklmn"; byte[] bytes = Encoding.UTF8.GetBytes(str); MemoryStream memoryStream= new MemoryStream(); memoryStream.Write(bytes, 0, bytes.Length); memoryStream.Seek(0, SeekOrigin.Begin); StreamReader streamReader= new StreamReader(memoryStream); char[] readbuff = new char[5]; int count = 0; do { count = streamReader.Read(readbuff, 0, readbuff.Length); string outputData = new string(readbuff, 0, count); } while (count > 0); } } }
序列化
例:
using System; using System.Collections.Generic; using System.IO; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary; using System.Xml.Serialization; namespace P25ConsoleApp2 { public class Program { static void Main(string[] args) { List<Person> person1 = new List<Person>() { new Person { Name = "一二", Age = 18 }, new Person { Name = "三四", Age = 19 } }; //二进制序列化 string file = @"person1BinarySerializeFile.dat"; FileStream fs = new FileStream(file, FileMode.Create); BinaryFormatter binaryFormatter = new BinaryFormatter(); binaryFormatter.Serialize(fs, person1); fs.Close(); //二进制反序列化 fs = new FileStream(file, FileMode.Open); List<Person> person2 = binaryFormatter.Deserialize(fs) as List<Person>; fs.Close(); //XML序列化 file = @"person1XmlSerializeFile.xml"; XmlSerializer xmlSerializer = new XmlSerializer(typeof(List<Person>)); fs = new FileStream(file, FileMode.Create); StreamWriter sw = new StreamWriter(fs, System.Text.Encoding.UTF8); xmlSerializer.Serialize(sw, person2); sw.Close(); fs.Close(); //XML反序列化 fs = new FileStream(file, FileMode.Open); person2 = xmlSerializer.Deserialize(fs) as List<Person>; fs.Close(); } [Serializable] //类被标记该特性后,就可以被序列化 public class Person { public string Name { get; set; } public int Age { get; set; } public override string ToString() { return $"{Name};{Age}"; } } } }
文件
File 静态类
Directory 静态类
Path 静态类
FileSystemInfo 抽象类
FileInfo
DirectoryInfo
FileSystemWatcher 类
监控文件和目录的改动
EnableRaisingEvents 属性为 true 时,开始监视。
例:
FileSystemWatcher watcher = new FileSystemWatcher(path, filter); //path 目录,必须存在;filter 文件通配字符串 watcher.IncludeSubdirectories = false; //是否包括子目录 watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite | NotifyFilters.CreationTime; //根据需要监控需要的操作 watcher.Changed += watcher_Changed; watcher.Created += watcher_Created; watcher.Deleted += watcher_Deleted; watcher.Error += watcher_Error; watcher.Renamed += watcher_Renamed; watcher.EnableRaisingEvents = true; //开始 watcher.EnableRaisingEvents = false; //停止 watcher.Dispose();
使用例:
给路径添加目录分隔符的方法
public static string AddDirectorySeparatorChar(string directory) { return string.IsNullOrWhiteSpace(directory) ? directory : directory.EndsWith(System.IO.Path.DirectorySeparatorChar.ToString()) || directory.EndsWith(System.IO.Path.AltDirectorySeparatorChar.ToString()) || directory.EndsWith(System.IO.Path.VolumeSeparatorChar.ToString()) ? directory : directory + System.IO.Path.DirectorySeparatorChar; }
文件重命名
FileInfo 的 MoveTo 方法
删除
删除前最好设置为正常属性
例:
File.SetAttributes(filePath, FileAttributes.Normal);
获取系统路径
例:
System.Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)
System.Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)
用作当前非漫游用户使用的应用程序特定数据的公共储存库的目录。 例: C:\Users\用户名\AppData\Local
获取应用程序基目录
例:
System.AppDomain.CurrentDomain.BaseDirectory
使用资源管理器定位到文件
例:
Process.Start("explorer.exe", string.Format("/select, \"{0}\"", filePath));
长路径支持
注册表
结构
树状
组成
键(key)
HKEY_CLASSES_ROOT
定义文档的类型(或类)以及与那些类型关联的属性
HKEY_CURRENT_USER
包含有关当前用户首选项的信息
HKEY_LOCAL_MACHINE
包含本地计算机的配置数据
HKEY_USERS
包含有关默认用户配置的信息
HKEY_CURRENT_CONFIG
包含有关非用户特定的硬件的配置信息
HKEY_PERFORMANCE_DATA
包含软件组件的性能信息
子键
每个键都至少包含一个值项(字符串类型的默认值项(Default))
值项(value entry)
名称
数值
类型
字符串(REG_SZ)
二进制(REG_BINARY)
DWORD(REG_DWORD)
QWORD(REG_QWORD)
多字符串(REG_MULTI_SZ)
可扩充字符(REG_EXPAND_SZ)
操作类
例:
RegistryKey testRootKey = Registry.CurrentUser.OpenSubKey("Software", true); //打开子键,true:可写 RegistryKey testKey = testRootKey.CreateSubKey("测试键\\子键"); //打开或创建子键(可写) testKey.SetValue("值项", "值", RegistryValueKind.String); object itemValue = testKey.GetValue("值项"); string[] keyItemsNames = testKey.GetValueNames(); testRootKey.DeleteSubKeyTree("测试键"); testKey.Close(); //关闭并保存修改
顶层键
Registry. LocalMachine 属性等
命名空间:Microsoft.Win32
属性值类型:RegistryKey
子健
RegistryKey 实例的
OpenSubKey(~) 方法
CreateSubKey(~) 方法
DeleteSubKey(~) 方法
DeleteSubKeyTree(~) 方法
递归删除子键
GetValueNames()
GetValue(~)
SetValue(~)
Close()
XML 操作
概念
一种描述数据的文本文件
用途
Web 应用程序之间传递数据
基于 XML 的 Web Service 用其交换数据、与相互调用
文档格式化、数据交换、数据存储、数据库操作
编程处理方式
DOM
概念
即 文档对象模型(Document Object Mode)
解析器读取全部文档,并在内存建立有层次的树
类
System.Xml.XmlDocument
例:
XmlDocument xmlfile = new XmlDocument(); //实例化 xmlfile.Load(xmlFilepath); //加载文件 XmlNode root = xmlfile.SelectSingleNode("根元素标签"); //取根元素 XmlNode ele = root.SelectSingleNode("元素标签"); //取元素 XmlNodeList eles = root.SelectSingleNode("元素标签").ChildNodes; //取所有子元素 foreach (XmlNode item in eles) //遍历子元素 { } string txt = ele.InnerText; //元素内容文本 txt = ((XmlElement)ele).GetAttribute("关键字"); //通过元素属性关键字读其值 ((XmlElement)ele).SetAttribute("关键字", "值"); //通过元素属性关键字写其值 XmlElement nele = xmlfile.CreateElement("元素标签"); //创建元素 ele.AppendChild(nele); //添加元素 ele.RemoveChild(ele.SelectSingleNode("需移除的元素标签")); //移除元素 xmlfile.Save(filepath); //保存
Linq to XML
类
System.Xml.LinqXDocument
SAX
概念
即 用于 XML 解析的简单 API(Simple API for Xml parsing)
逐行读取文档,依次验证元素
类
System.Xml.XmlTextReader、System.Xml.XmlTextWriter
JSON 操作
概念
一种轻量级的数据交换格式
JavaScript Object Notation,JS对象标记
编程处理方式
使用 NuGet 包:
Newtonsoft.Json
异常
异常处理语句格式
例:
try { int a = 0; a = 1 / a; } catch (DivideByZeroException ex) when (ex.InnerException != null) //捕获特定的异常,catch 块放前面。C#6 还可以支持 when 子句 { throw ex; //重新抛出异常,异常对象的类型只能为 Exception 类或其派生类 } catch (DivideByZeroException ex) { throw new Exception("除0了", ex); //“异常的链接”:重抛新异常,新异常构造时传入了当前异常。当前异常可从新异常的 InnerException 属性获取 } catch (Exception ex) //捕获所有类型的异常 { throw; //重抛当前异常 } finally //无论异常是否处理,最终都会执行的块 { //不能使用 return、break、continue、goto 等跳出 finally 范围 }
未处理异常捕获
线程异常
设置 System.AppDomain. CurrentDomain.UnhandledException 事件
Task异常
设置 System.Threading.Tasks. TaskScheduler.UnobservedTaskException 事件
UI异常
WinForm
关闭跨线程调用异常
System.Windows.Forms. Control.CheckForIllegalCrossThreadCalls = false;
不推荐。
WPF
设置 System.Windows.Application. Current.DispatcherUnhandledException 事件
算术溢出
C# 默认不进行溢出检查
可以使用 checked {语句} 进行溢出检查
可以使用 unchecked {语句} 取消溢出检查
对于 decimal 类型
算术溢出总是引发 OverflowException
被除零总是引发 DivideByZeroException
测试
程序追踪
Debug、Trace 类
System.Diagnostics
属性
Listeners
元素类型如:TextWriterTraceListener
单元测试
VS项目
新建“单元测试项目”
特性
[TestClass]
测试类
[TestMethod]
测试方法(/测试用例)
断言
Assert 类
方法
IsTrue、IsFalse 等
异步
多线程
创建
命名空间:System.Threading
类:Thread
例: Thread thr = new Thread(new ThreadStart(obj.Fun)); thr.Start(~);
获取当前线程ID
Thread.CurrentThread.ManagedThreadId
休眠
Thread.Sleep(~)
同步
lock语句与Monitor类
lock语句
作用
将语句块标记为临界区
格式
lock (对象或表达式) {......}
最好锁定 private 对象
当同步对共享资源的线程访问时,请锁定专用对象实例(例如,private readonly object balanceLock = new object();)或另一个不太可能被代码无关部分用作 lock 对象的实例。 避免对不同的共享资源使用相同的 lock 对象实例,因为这可能导致死锁或锁争用。 具体而言,请避免将以下类型用作 lock 对象: 1.值类型 2.this(调用方可能将其用作 lock)。 3.Type 实例(因为这些对象可以通过 typeof 运算符或反射获取)。 4.常量字符串实例。常量字符串会被公共语言运行库 (CLR)“暂留”。 这意味着整个程序中任何给定常量字符串都只有一个实例。若需要锁定字符串,注意写为 lock (string.Intern(str)),防止动态创建的字符串实例不同。 尽可能缩短持有锁的时间,以减少锁争用。
等价于: bool lockTaken false; try { System.Threading.Monitor.Enter(对象或表达式, ref lockTaken); if (lockTaken) { ...... } } finally { if (lockTaken) System.Threading.Monitor.Exit(对象或表达式); }
例: class Num { private Object thisLock = new Object(); //或 private static readonly Object thisLock = new Object(); public void Test() { lock (thisLock) { ...... } } }
thr.Join(~) 方法
等到线程终止后返回
Interlocked类
为多个线程共享的变量提供原子操作
WaitHandle类
等待句柄,是一个抽象类。是事件、互斥对象、信号量等的基类
事件
AutoResetEvent类
自动重置事件
主要方法
Set()
触发事件
Reset()
解除触发事件
WaitOne(~)
等待事件
例:
using System; using System.Threading; namespace AutoResetEventTest { internal class Program { static void Main(string[] args) { AutoResetEvent autoResetEvent = new AutoResetEvent(false); //创建 Thread thread1 = new Thread(() => { int id = Thread.CurrentThread.ManagedThreadId; Console.WriteLine($"线程{id}即将触发事件..."); Thread.Sleep(2000); autoResetEvent.Set(); //触发事件 Console.WriteLine($"线程{id}已触发事件。"); }); Thread thread2 = new Thread(() => { int id = Thread.CurrentThread.ManagedThreadId; Console.WriteLine($"线程{id}等待事件..."); autoResetEvent.WaitOne(); //等待事件 Console.WriteLine($"线程{id}已收到事件。"); }); thread1.Start(); thread2.Start(); Console.ReadKey(); autoResetEvent.Close(); //关闭 } } }
ManualResetEvent类
手动重置事件
读写锁
ReaderWriterLock类
定义支持单个写线程和多个读线程的锁
ReaderWriterLockSlim类
定义支持单个写线程和多个读线程的锁
用于 ReaderWriterLockSlim 保护由多个线程读取并一次写入一个线程的资源。 ReaderWriterLockSlim 允许多个线程处于读取模式,允许一个线程处于具有锁的独占所有权的写入模式,并允许具有读取访问权限的线程处于可升级的读取模式,线程可以升级到写入模式,而无需放弃对资源的读取访问权限。
例:
public class SynchronizedCache { private ReaderWriterLockSlim cacheLock = new ReaderWriterLockSlim(); //创建 private Dictionary<int, string> innerCache = new Dictionary<int, string>(); public int Count { get { return innerCache.Count; } } public string Read(int key) { cacheLock.EnterReadLock(); //进入读锁 try { return innerCache[key]; } finally { cacheLock.ExitReadLock(); } } public void Add(int key, string value) { cacheLock.EnterWriteLock(); //进入写锁 try { innerCache.Add(key, value); } finally { cacheLock.ExitWriteLock(); } } public bool AddWithTimeout(int key, string value, int timeout) { if (cacheLock.TryEnterWriteLock(timeout)) { try { innerCache.Add(key, value); } finally { cacheLock.ExitWriteLock(); } return true; } else { return false; } } public AddOrUpdateStatus AddOrUpdate(int key, string value) { cacheLock.EnterUpgradeableReadLock(); //进入可升级(到写的)读锁 try { string result = null; if (innerCache.TryGetValue(key, out result)) { if (result == value) { return AddOrUpdateStatus.Unchanged; } else { cacheLock.EnterWriteLock(); try { innerCache[key] = value; } finally { cacheLock.ExitWriteLock(); } return AddOrUpdateStatus.Updated; } } else { cacheLock.EnterWriteLock(); try { innerCache.Add(key, value); } finally { cacheLock.ExitWriteLock(); } return AddOrUpdateStatus.Added; } } finally { cacheLock.ExitUpgradeableReadLock(); } } public void Delete(int key) { cacheLock.EnterWriteLock(); try { innerCache.Remove(key); } finally { cacheLock.ExitWriteLock(); } } public enum AddOrUpdateStatus { Added, Updated, Unchanged }; ~SynchronizedCache() { if (cacheLock != null) cacheLock.Dispose(); } }
进程级的
Mutex类
互斥对象,一般用于进程间同步
例:
using System; using System.Threading; namespace MutexTest { internal class Program { static void Main(string[] args) { Mutex semaphore = new Mutex(false, "MutexTest", out bool isCreatedNew); //创建 if (!isCreatedNew || !semaphore.WaitOne(500)) //申请 { Console.WriteLine("程序实例已达上限"); Thread.Sleep(500); return; } Console.WriteLine("程序运行中..."); Console.ReadKey(); semaphore.ReleaseMutex(); //释放 } } }
Semaphore类
信号量,一般用于在进程间限制可同时访问某一资源的进(线)程数
例:
using System; using System.Threading; namespace SemaphoreTest { internal class Program { static void Main(string[] args) { Semaphore semaphore = new Semaphore(1, 3, "SemaphoreTest"); //创建 if (!semaphore.WaitOne(500)) //申请 { Console.WriteLine("程序实例已达上限"); Thread.Sleep(500); return; } Console.WriteLine("程序运行中..."); Console.ReadKey(); semaphore.Release(); //释放 } } }
自旋
这里的自旋,可以理解成类似如下的代码: while (condition) { //... }
自旋锁
比 lock、Mutex 等的开销小。
SpinLock 结构体
例:
using System; using System.Threading; using System.Threading.Tasks; namespace SpinLockTest { internal class Program { static void Main(string[] args) { SpinLock spinLock = new SpinLock(); //创建 Action action = () => { bool lockTaken = false; for (int i = 0; i < 10; i++) { try { int id = Thread.CurrentThread.ManagedThreadId; lockTaken = false; //进入前初始为未锁定 Console.WriteLine($"线程:{id} 准备进入锁..."); spinLock.Enter(ref lockTaken); //进入 if (1 == Thread.CurrentThread.ManagedThreadId && i > 3 && i < 7) { Console.WriteLine($"线程:{id} 等待继续"); Console.ReadKey(); Console.WriteLine($"线程:{id} 继续..."); } Console.WriteLine($"线程:{id},值:{i}"); } finally { if (lockTaken) spinLock.Exit(); //离开 } } }; Parallel.Invoke(action, action, action); Console.ReadKey(); } } }
自旋等待
SpinWait 结构体
例:
using System; using System.Threading; using System.Threading.Tasks; public class App { private static bool isEnvironmentInit = false; static void Main(string[] args) { Task.Run(() => { DoWork(); }); Thread.Sleep(3000); isEnvironmentInit = true; Console.WriteLine("环境初始化完成"); Console.ReadLine(); } private static void DoWork() { SpinWait.SpinUntil(() => isEnvironmentInit, 10000); //自旋等待。是对 while 循环加 Thread.Sleep(1); 等类似的等待代码的优化 Console.WriteLine("环境已在主线程中初始化..."); Console.WriteLine("业务逻辑代码..."); } }
线程池
ThreadPool类
使用ThreadPool.QueueUserWorkItem(~)提交任务,系统根据任务自动创建线程执行
线程计时器
Timer类
在单独线程中定期执行任务
提示:Timer对象使用完毕后,应调用Dispose()方法释放资源
扩展
多媒体毫秒级高精度定时器
例:
定时器类
using System; using System.Runtime.InteropServices; namespace HighPrecisionTimer { /// <summary> /// 定时器性能数据。 /// </summary> public struct TimerCaps { /// <summary> /// 最小周期(ms) /// </summary> public uint wPeriodMin; /// <summary> /// 最大周期(ms) /// </summary> public uint wPeriodMax; } public enum TimerEventType01 { /// <summary> /// 单次执行 /// </summary> TIME_ONESHOT = 0x0000, /// <summary> /// 循环执行 /// </summary> TIME_PERIODIC = 0x0001, } public enum TimerEventType02 { /// <summary> /// 定时器到期时,调用回调方法。这是默认值 /// </summary> TIME_CALLBACK_FUNCTION = 0x0000, /// <summary> /// 定时器到期时,调用 SetEvent 函数 /// </summary> TIME_CALLBACK_EVENT_SET = 0x0010, /// <summary> /// 定时器到期时,调用 PulseEvent 函数 /// </summary> TIME_CALLBACK_EVENT_PULSE = 0x0020, /// <summary> /// 防止在调用 timeKillEvent 函数之后激发事件 /// </summary> TIME_KILL_SYNCHRONOUS = 0x0100, } /// <summary> /// 高精度定时器。 /// </summary> public class HPTimer { #region 字段 /// <summary> /// 系统的定时器性能数据 /// </summary> private static TimerCaps _caps; /// <summary> /// 分辨率(ms) /// </summary> private uint _resolution; /// <summary> /// 定时器 ID /// </summary> private uint _uTimerID; /// <summary> /// 定时器到期时回调函数类型 /// </summary> /// <param name="uTimerID">定时器 ID</param> /// <param name="uMsg">预留,不使用</param> /// <param name="dwUser">用户数据</param> /// <param name="dw1">预留,不使用</param> /// <param name="dw2">预留,不使用</param> private delegate void TimerCallback(uint uTimerID, uint uMsg, uint dwUser, uint dw1, uint dw2); /// <summary> /// 定时器到期时回调函数 /// </summary> private TimerCallback _tickCallback; #endregion #region 属性 private uint interval; /// <summary> /// 嘀嗒间隔(ms) /// </summary> public uint Interval { get { return interval; } set { if (value < _caps.wPeriodMin) interval = _caps.wPeriodMin; else if (value > _caps.wPeriodMax) interval = _caps.wPeriodMax; else interval = value; } } /// <summary> /// 是否在运行中 /// </summary> public bool Running { get; private set; } #endregion #region 事件 /// <summary> /// 嘀嗒事件 /// </summary> public event Action Tick; #endregion #region Windows API 接口 /// <summary> /// 查询设备支持的多媒体定时器性能 /// </summary> /// <param name="ptc">定时器性能数据结构体指针</param> /// <param name="cbtc">定时器性能数据结构体大小</param> /// <returns></returns> [DllImport("winmm.dll", EntryPoint = "timeGetDevCaps")] private static extern uint TimeGetDevCaps(ref TimerCaps ptc, uint cbtc); /// <summary> /// 启动多媒体定时器事件 /// </summary> /// <param name="uDelay">事件延迟/嘀嗒间隔</param> /// <param name="uResolution">分辨率</param> /// <param name="fptc">定时器到期时的回调</param> /// <param name="dwUser">用户数据</param> /// <param name="fuEvent">计时器嘀嗒事件类型</param> /// <returns>若成功返回定时器 ID</returns> [DllImport("winmm.dll", EntryPoint = "timeSetEvent")] private static extern uint TimeSetEvent(uint uDelay, uint uResolution, TimerCallback fptc, uint dwUser, uint fuEvent); /// <summary> /// 终止多媒体定时器事件 /// </summary> /// <param name="id">定时器 ID</param> /// <returns></returns> [DllImport("winmm.dll", EntryPoint = "timeKillEvent")] private static extern uint TimeKillEvent(uint uTimerID); #endregion static HPTimer() { TimeGetDevCaps(ref _caps, (uint)Marshal.SizeOf(typeof(TimerCaps))); } public HPTimer() { Running = false; interval = _caps.wPeriodMin; _resolution = _caps.wPeriodMin; _tickCallback = new TimerCallback(TimerEventCallback); } ~HPTimer() { TimeKillEvent(_uTimerID); } #region 内部方法 private void TimerEventCallback(uint uTimerID, uint uMsg, uint dwUser, uint dw1, uint dw2) { Tick?.Invoke(); } #endregion #region 公开方法 /// <summary> /// 启动 /// </summary> /// <exception cref="Exception"></exception> public void Start() { if (!Running) { _uTimerID = TimeSetEvent(interval, _resolution, _tickCallback, 0, (int)TimerEventType01.TIME_PERIODIC | (int)TimerEventType02.TIME_KILL_SYNCHRONOUS); if (_uTimerID is 0) throw new Exception("启动定时器失败"); Running = true; } } /// <summary> /// 停止 /// </summary> public void Stop() { if (Running) { TimeKillEvent(_uTimerID); Running = false; } } #endregion } }
应用
using System; using System.Diagnostics; using HighPrecisionTimer; namespace MultimediaTimer { class Program { static void Main() { Process process = Process.GetCurrentProcess(); process.PriorityClass = ProcessPriorityClass.RealTime; //设置进程优先级 HPTimer timer = new HPTimer(); //创建高精度定时器 timer.Interval = 1000; timer.Tick += Timer_Ticked; timer.Start(); Console.ReadKey(); timer.Stop(); } private static void Timer_Ticked() { Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff}"); } } }
集合的线程安全性
非泛型集合
IsSynchoronized属性指示是否线程安全
SyncRoot属性获取可同步对集合的访问的对象
供lock语句等使用
Synchronized(~)静态方法获取一个线程安全的包装对象
泛型集合
命名空间System.Collections.Concurrent中提供了线程安全的“并发集合类”
例:
使用 BlockingCollection 解决生产者消费者问题
using System; using System.Collections.Concurrent; using System.Threading; using System.Threading.Tasks; public class App { static void Main(string[] args) { BlockingCollection<string> blockingCollection = new BlockingCollection<string>(); //创建阻塞集合 bool isStop = false; var producer = Task.Run(() => { for (int i = 0; !isStop; i++) { SpinWait.SpinUntil(() => false, 1000); string data = i.ToString(); blockingCollection.Add(data); //生产数据 Console.WriteLine($"已生产数据:{data}"); } blockingCollection.CompleteAdding(); //生产完成 }); var consumer1 = Task.Run(() => { foreach (var item in blockingCollection.GetConsumingEnumerable()) //阻塞迭代消费数据 { Console.WriteLine($"消费者1已消费数据:{item}"); } }); var comsumer2 = Task.Run(() => { foreach (var item in blockingCollection.GetConsumingEnumerable()) //阻塞迭代消费数据 { Console.WriteLine($"消费者2已消费数据:{item}"); } }); Console.WriteLine("若要停止请按回车键..."); Console.ReadKey(); isStop = true; Task.WaitAll(producer, consumer1, comsumer2); Console.WriteLine("已停止。"); Console.ReadKey(); } }
窗体中的线程
其他线程中操作界面
需要将这个操作的任务交给创建界面的线程处理
通过界面对象(窗体或控件)的“同步”Invoke(~)或“异步”BeginInvoke(~)方法完成任务提交
可以通过控件的InvokeRequired属性判断是否需要提交
BackgroundWorker类
调用其RunWorkerAsync(~)方法,激发DoWork事件执行任务(该事件相对于界面线程是异步的)。方法的参数还会传给事件EventArgs参数的Argument成员。
调用其ReportProgress(~)方法,激发ProgressChanged事件报告任务进度任务。方法的参数还会传给事件EventArgs参数的ProgressPercentage成员。
任务完成后,会激发RunWorkerCompleted事件表示任务完成。 任务正常完成时,DoWork事件EventArgs参数的Result成员还会传给RunWorkerCompleted事件EventArgs参数的Result成员。 任务被取消时,RunWorkerCompleted事件EventArgs参数的Cancelled成员值为true。 任务出现异常时,RunWorkerCompleted事件EventArgs参数的Error成员值不为null。
调用其CancelAsync()方法,取消任务执行。
并行编程
概念
并行程序一般将一个任务分解为多个相互独立的子任务,每个子任务分别由不同的处理器执行,最后,综合各子任务的结果形成整个任务的结果。
C#从.Net Framework 4(最好使用4.5以上)起引入了新的类库Task Parallel Libary (任务并行库),是命名空间System.Threading和System.Threading.Tasks中的一组公共类型API。
Task类
创建、执行任务
例: Task<TResult> task = new TaskFactory().StartNew(~); 或 Task<TResult> task = Task.Run(~);
返回值类型
Task
表示一个异步操作
Task<TResult>
表示一个可以返回值的异步操作
其中,泛型参数类型是具体任务方法的返回值类型
要得到任务结果,可以使用其Result属性,该属性的get访问器会阻塞调用线程直到任务返回。 例:TResult result = task.Result;
等待任务完成
task.Wait(~);
等待所有任务完成
Task.WaitAll(~)
为当前task指定一个完成后的延续任务
task.ContinueWith(~)
多个task的异常合并处理
例: try { Task.Wait(~); } catch (AggregateException ex) { foreach (Exception inner in ex.InnerExceptions) { ...... } }
取消任务
CancellationTokenSource
例:
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); public void TaskTest() { Status = "0"; Task.Run(() => { Status = "1"; Thread.Sleep(1000); Status = "2"; Thread.Sleep(600); cancellationTokenSource.Token.ThrowIfCancellationRequested(); //如已取消,可抛出异常以中止Task执行 Thread.Sleep(1000); Status = "3"; }, cancellationTokenSource.Token).ContinueWith(t => //如果 cancellationTokenSource.Token 为取消状态,则 Task 不会执行 { Thread.Sleep(1000); Status = t.Exception?.InnerException?.Message; }); Task.Run(() => { Thread.Sleep(1500); cancellationTokenSource.Cancel(); }); }
Parallel类
任务并行
Parallel.Invoke(~)
数据并行
针对范围
Parallel.For(~)
针对集合
Parallel.ForEach(~)
停止执行
上面2个循环委托参数中声明一个ParallelLoopState类型的参数,再在其方法体内调用该参数的Stop()方法
并行Linq (PLinq)
命名空间 System.Linq
对任一Enumerable对象使用AsParallel()方法即可得到并行的查询
例: int[] sor = new int[10]; var q = sor.AsParallel().Where(n => n>5);
异步编程
概念
将一些耗时的操作用别的线程执行,必要时等待其结果
async和await
是C#5.0引入的关键字
async是一个修饰符,表明一个方法是异步方法
await是一个操作符,表示等待异步方法执行完毕,并取得Result,实现同步操作
内部有await的方法,必须声明为async方法
例: Task<double> CalcuAsync(int n) { return Task.Run(() => { //由Task线程执行 double s = 1; //计算操作 return s; }); } async void CalcuNumberAsync() //异步方法的返回值类型必须为void、Task、Task<T>、或类似Task的类型 { Task<double> calcuNumberTask = CalcuAsync(123); //其他不依赖异步任务返回值的操作 //由调用方线程执行,之后控制权直接交给上层调用者 double result = await calcuNumberTask; //await之后的操作将由Task线程完成执行 }
返回值类型为 Task、Task<T> 时, 若调用者进行了 await 操作,则 Task / Task<T> 中的异常可以传播给调用者
异步方法与事件
如 System.Net.WebClient 类中的 DownloadStringAsync 方法与 DownloadStringCompleted 事件
委托异步调用
委托的BeginInvoke方法进行异步调用
委托的EndInvoke方法等待异步调用完成
例: double Calcu(int n) { double s = 1; //计算操作 return s; } void CalcuNumber() { Func<int, double> dFunc = Calcu; IAsyncResult calcuRes = dFunc.BeginInvoke(123, null, null); //该方法第3个参数会传给第2个参数即回调方法中参数的AsyncState属性 //其他不依赖异步调用方法返回值的操作 double result = dFunc.EndInvoke(calcuRes); //等待异步调用完成并取得结果 }
进程间通信
参考
https://www.cnblogs.com/1996-Chinese-Chen/p/15780813.html https://www.cnblogs.com/weskynet/p/16391095.html
共享内存
命名空间
System.IO.MemoryMappedFiles
类
MemoryMappedFile
例:
var bufferMap = MemoryMappedFile.CreateNew(BufferMapName, MaxLength, MemoryMappedFileAccess.ReadWrite); //创建 var accessor = bufferMap.CreateViewAccessor(); //获取视图访问器 byte[] data = new byte[1024]; accessor.WriteArray<byte>(0, data, 0, data.Length); //写 int readCount = accessor.ReadArray<byte>(0, data, 0, data.Length); //读
命名管道
命名空间
System.IO.Pipes
主要的类
NamedPipeClientStream
主要构造方法: public NamedPipeClientStream (string serverName, string pipeName, System.IO.Pipes.PipeDirection direction, System.IO.Pipes.PipeOptions options, System.Security.Principal.TokenImpersonationLevel impersonationLevel, System.IO.HandleInheritability inheritability); 参数: serverName String 要连接的远程计算机的名称,或者为“.”,以指定本地计算机。 pipeName String 管道的名称。 direction PipeDirection 确定管道方向的枚举值之一。 options PipeOptions 确定如何打开或创建管道的枚举值之一。 impersonationLevel TokenImpersonationLevel 确定安全模拟级别的枚举值之一。 inheritability HandleInheritability 确定基础句柄是否将由子进程继承的枚举值之一。
NamedPipeServerStream
主要构造方法: public NamedPipeServerStream (string pipeName, System.IO.Pipes.PipeDirection direction, int maxNumberOfServerInstances, System.IO.Pipes.PipeTransmissionMode transmissionMode, System.IO.Pipes.PipeOptions options, int inBufferSize, int outBufferSize); 参数: pipeName String 管道的名称。 direction PipeDirection 确定管道方向的枚举值之一。 maxNumberOfServerInstances Int32 共享同一名称的服务器实例的最大数量。 可以为此值传递 MaxAllowedServerInstances。 transmissionMode PipeTransmissionMode 确定管道传输模式的枚举值之一。 options PipeOptions 确定如何打开或创建管道的枚举值之一。 inBufferSize Int32 一个大于 0 的正值,指示输入缓冲区大小。 outBufferSize Int32 一个大于 0 的正值,指示输出缓冲区大小。
例:
客户端
using System; using System.Collections.Generic; using System.Text; using System.IO.Pipes; namespace PipeClient { class Program { static void Main(string[] args) { NamedPipeClientStream pipe = null; try { Console.WriteLine("连接服务器..."); pipe = new NamedPipeClientStream(".", "testPipe", PipeDirection.InOut, PipeOptions.Asynchronous); //初始化 NamedPipeClientStream 类的新实例 pipe.Connect(); //建立与管道服务器的连接 if (pipe.IsConnected) { Console.WriteLine("已连接!"); while (true) { Console.Write("输入>"); string sendMsg = Console.ReadLine(); byte[] sendData = Encoding.UTF8.GetBytes(sendMsg); pipe.Write(sendData, 0, sendData.Length); //发送数据 pipe.Flush(); Console.WriteLine($"发:{sendMsg}"); List<byte> readData = new List<byte>(); byte[] readBuffer = new byte[4096]; bool available = true; do { int readCount = pipe.Read(readBuffer, 0, readBuffer.Length); //接收数据 if (readCount < readBuffer.Length) { byte[] newBuffer = new byte[readCount]; Buffer.BlockCopy(readBuffer, 0, newBuffer, 0, readCount); readBuffer = newBuffer; available = false; } readData.AddRange(readBuffer); } while (available); //是否还有可用的数据 string readMsg = Encoding.UTF8.GetString(readData.ToArray()); Console.WriteLine($"收:{readMsg}"); if (sendMsg.Equals("exit", StringComparison.OrdinalIgnoreCase)) { break; } } } } catch (Exception ex) { Console.WriteLine($"异常:{ex.Message}"); } finally { if (null != pipe) { pipe.Close(); //关闭 NamedPipeClientStream 连接并释放所有关联的资源 } } Console.WriteLine("按任意键退出..."); Console.Read(); } } }
服务端
using System; using System.Collections.Generic; using System.Text; using System.Threading; using System.IO.Pipes; namespace PipeServer { class Program { static void Main(string[] args) { NamedPipeServerStream pipe = null; try { pipe = new NamedPipeServerStream("testPipe", PipeDirection.InOut, 2, PipeTransmissionMode.Byte, PipeOptions.Asynchronous); //初始化 NamedPipeServerStream 类的新实 while (true) { CheckConnection(pipe); Thread.Sleep(1); } } catch (Exception ex) { Console.WriteLine($"侦听异常:{ex.Message}"); } finally { if (null != pipe) { pipe.Close(); //关闭 NamedPipeServerStream 连接并释放所有关联的资源 } } Console.WriteLine("按任意键退出..."); Console.Read(); } static void CheckConnection(object obj) { try { NamedPipeServerStream server = obj as NamedPipeServerStream; Console.WriteLine("等待客户端连接..."); server.WaitForConnection(); //等待 Client 连接 Console.WriteLine("检测到客户端连接..."); ConnectionHandle(server); } catch (Exception ex) { Console.WriteLine($"检测客户端连接过程异常:{ex.Message}"); } } static void ConnectionHandle(object obj) { NamedPipeServerStream pipe = obj as NamedPipeServerStream; try { Console.WriteLine($"接受客户端连接... 处理线程:{Thread.CurrentThread.ManagedThreadId}"); if (pipe.IsConnected) { Console.WriteLine("已连接!"); while (true) { List<byte> readData = new List<byte>(); byte[] readBuffer = new byte[4096]; bool available = true; do { int readCount = pipe.Read(readBuffer, 0, readBuffer.Length); //接收数据 if (readCount < readBuffer.Length) { byte[] newBuffer = new byte[readCount]; Buffer.BlockCopy(readBuffer, 0, newBuffer, 0, readCount); readBuffer = newBuffer; available = false; } readData.AddRange(readBuffer); } while (available); //是否还有可用的数据 string readMsg = Encoding.UTF8.GetString(readData.ToArray()); Console.WriteLine($"收:{readMsg} 处理线程:{Thread.CurrentThread.ManagedThreadId}"); string sendMsg = readMsg.ToUpper(); byte[] sendData = Encoding.UTF8.GetBytes(sendMsg); pipe.Write(sendData, 0, sendData.Length); //发送数据 pipe.Flush(); Console.WriteLine($"发:{sendMsg} 处理线程:{Thread.CurrentThread.ManagedThreadId}"); if (readMsg.Equals("exit", StringComparison.OrdinalIgnoreCase)) { break; } } } } catch (Exception ex) { Console.WriteLine($"连接异常:{ex.Message} 处理线程:{Thread.CurrentThread.ManagedThreadId}"); } finally { if (null != pipe) { pipe.Disconnect(); //关闭 NamedPipeServerStream 当前连接 } Console.WriteLine($"断开客户端连接。 处理线程:{Thread.CurrentThread.ManagedThreadId}"); } } } }
反射
Activator 类
包含特定的方法,用以在本地或从远程创建对象类型,或获取对现有远程对象的引用。
命名空间:System
例:
(1)创建实例
using System; using System.Reflection; namespace ConsoleApp1 { internal class Program { static void Main(string[] args) { IVehicle vehicle = new Car(); var type = vehicle.GetType(); //获取实例的类型信息 //type = typeof(Car); //获取类型的类型信息,效果一样 var obj = Activator.CreateInstance(type); //创建指定类型的实例 MethodInfo runMi = type.GetMethod("Run"); runMi.Invoke(obj, null); //调用指定实例的该方法 } } interface IVehicle { void Fill(); void Run(); void Stop(); } abstract class Vehicle : IVehicle { public void Fill() { Console.WriteLine("加油..."); } public abstract void Run(); public void Stop() { Console.WriteLine("停止!"); } } class Car : Vehicle { public override void Run() { Console.WriteLine($"{nameof(Car)} 开动..."); } public virtual void Music() { Console.WriteLine($"{nameof(Car)} 播放音乐..."); } } class Truck : Vehicle { public override void Run() { Console.WriteLine($"{nameof(Truck)} 开动..."); } } class RaceCar : Car { public override void Run() { Console.WriteLine($"{nameof(RaceCar)} 开动..."); } public override void Music() { Console.WriteLine($"{nameof(RaceCar)} 播放音乐..."); } } }
(2)反射的应用,依赖注入
using System; using System.Reflection; using Microsoft.Extensions.DependencyInjection; //一个依赖注入框架(IoC是控制反转,DI是实现IoC的一种手断。IoC容器可以理解为一个工厂,通过IoC容器,可以实现DI) namespace ConsoleApp1 { internal class Program { static void Main(string[] args) { IServiceCollection sc = new ServiceCollection(); //(1)创建服务容器(IoC容器) sc.AddTransient(typeof(IVehicle), typeof(Car)); //(2)注册一个服务(接口),及提供该服务的组件(类) sc.AddTransient(typeof(IVehicle), typeof(Truck)); sc.AddTransient(typeof(Driver)); var sp = sc.BuildServiceProvider(); //(3)创建一个服务提供者 var vehicle1 = sp.GetService<IVehicle>(); //(4)获取一个指定的服务的实例(这里是后注册的先被获取) vehicle1.Run(); var driver1 = sp.GetService<Driver>(); //创建Driver实例时,框架先会自动注入一个实现了IVehicle接口的实例(即依赖注入(方式:构造方法注入、属性注入、方法参数注入)) driver1.Drive(); } } interface IVehicle { void Fill(); void Run(); void Stop(); } class Driver { private IVehicle _vehicle; public Driver(IVehicle vehicle) { _vehicle = vehicle; } public void Drive() { _vehicle.Run(); } } abstract class Vehicle : IVehicle { public void Fill() { Console.WriteLine("加油..."); } public abstract void Run(); public void Stop() { Console.WriteLine("停止!"); } } class Car : Vehicle { public override void Run() { Console.WriteLine($"{nameof(Car)} 开动..."); } public virtual void Music() { Console.WriteLine($"{nameof(Car)} 播放音乐..."); } } class Truck : Vehicle { public override void Run() { Console.WriteLine($"{nameof(Truck)} 开动..."); } } class RaceCar : Car { public override void Run() { Console.WriteLine($"{nameof(RaceCar)} 开动..."); } public override void Music() { Console.WriteLine($"{nameof(RaceCar)} 播放音乐..."); } } }
(3)插件式编程
宿主程序
应用
//Program.cs using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using BabyStroller.SDK; namespace BabyStroller.App { internal class Program { static void Main(string[] args) { var folder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Animals"); var files = Directory.GetFiles(folder); var animalTypes = new List<Type>(); foreach (var file in files) { var assembly = Assembly.LoadFrom(file); //加载某个程序集 var types = assembly.GetTypes(); //通过反射获取类型信息 foreach (var type in types) { if (type.GetInterfaces().Contains(typeof(IAnimal))) //判断类型是否实现了指定接口 { if (type.GetCustomAttributes(false).Any(a => a.GetType() == typeof(UnfinishedAttribute))) //判断类型是否附加了指定特性 continue; animalTypes.Add(type); } } } while (true) { Console.Clear(); for (int i = 0; i < animalTypes.Count; i++) { Console.WriteLine($"{i + 1}: {animalTypes[i].Name}"); } Console.WriteLine("==========="); Console.WriteLine("请选择动物:"); int index = int.Parse(Console.ReadLine()); if (index > animalTypes.Count || index < 1) { Console.WriteLine("没有这样的动物。请重新输入!"); Console.WriteLine("按回车继续:"); Console.ReadLine(); continue; } Console.WriteLine("请输入次数:"); int times = int.Parse(Console.ReadLine()); var animalType = animalTypes[index - 1]; var obj = Activator.CreateInstance(animalType); //通过反射创建实例 var animal = obj as IAnimal; animal.Voice(times); Console.WriteLine("按回车继续:"); Console.ReadLine(); } } } }
SDK
//IAnimal.cs namespace BabyStroller.SDK { public interface IAnimal { void Voice(int times); } } //UnfinishedAttribute.cs using System; namespace BabyStroller.SDK { public class UnfinishedAttribute : Attribute { } }
插件
// using System; using BabyStroller.SDK; namespace BabyStroller.PlugIn.A { [Unfinished] public class Cat : IAnimal { public void Voice(int times) { for (int i = 0; i < times; i++) { Console.WriteLine("喵"); } } } } // using System; using BabyStroller.SDK; namespace BabyStroller.PlugIn.A { public class Sheep : IAnimal { public void Voice(int times) { for (int i = 0; i < times; i++) { Console.WriteLine("咩"); } } } } // using System; using BabyStroller.SDK; namespace BabyStroller.PlugIn.B { [Unfinished] public class Cow : IAnimal { public void Voice(int times) { for (int i = 0; i < times; i++) { Console.WriteLine("哞"); } } } } // using System; using BabyStroller.SDK; namespace BabyStroller.PlugIn.B { public class Dog : IAnimal { public void Voice(int times) { for (int i = 0; i < times; i++) { Console.WriteLine("汪"); } } } }
Assembly 类
命名空间:System.Reflection
静态方法
GetAssembly(Type type)
获取类型所在程序集
GetExecutingAssembly()
获取当前执行的代码所在程序集
GetEntryAssembly()
获取可执行程序集
Attribute
Attribute 是元数据的一种
Java 中叫做 Annotation
应用
判断是否为子类、实现类
例:
type.IsSubclassOf(baseType) || type.GetInterfaces().Contains(TargetType)
获取 exe 程序集路径
例:
Assembly.GetEntryAssembly().Location
网络
服务器搭建
Windows
使用 IIS
Internet Information Services (IIS 互联网信息服务)管理器
步骤
1.控制面板->程序和功能->启用或关闭Windows功能->(全)勾选Internet Information Services; 2.控制面板->系统和安全->管理工具->打开 Internet Information Services (IIS)管理器 建站; 注意: 物理路径勿选C盘,防止权限问题; 参考: https://www.bilibili.com/read/cv17850836
Linux
使用 Nginx
命名空间
System.Net
为当前网络采用的多种协议提供简单的编程接口。 此命名空间中的 System.Net.WebRequest 和 System.Net.WebResponse 类是可插入协议的基础。
主要类
IPAddress
提供网际协议 (IP) 地址
EndPoint
标识网络地址
主要派生类
DnsEndPoint
将网络终结点表示为主机名或 IP 地址和端口号的字符串表示形式
IPEndPoint
将网络端点表示为 IP 地址和端口号
Dns
提供简单的域名解析功能
IPHostEntry
为 Internet 主机地址信息提供容器类
WebProxy
包含 WebRequest 类的 HTTP 代理设置
Cookie
提供一组用于管理 Cookie 的属性和方法
NetworkCredential
为基于密码的身份验证方案(如基本、简要、NTLM 和 Kerberos 身份验证)提供凭据
WebRequest
对统一资源标识符 (URI) 发出请求
主要属性
Method
在此请求中使用的协议方法
可使用 WebRequestMethods.Http 类中的静态字段
RequestUri
与请求关联的 Internet 资源的 URI
Headers
与请求关联的标头名称/值对的集合
类型:WebHeaderCollection
CachePolicy
此请求的缓存策略
ContentType
所发送的请求数据的内容类型
ContentLength
所发送的请求数据的内容长度
Credentials
对 Internet 资源请求进行身份验证的网络凭据
CredentialCache
为多个凭据提供存储
UseDefaultCredentials
一个 Boolean 值,该值控制 DefaultCredentials 是否随请求一起发送
DefaultWebProxy
获取或设置全局 HTTP 代理
Proxy
用于访问此 Internet 资源的网络代理
Timeout
请求超时之前的时间长度(以毫秒为单位)
主要方法
Create(~)
为指定的 URI 方案初始化新的 WebRequest 实例
CreateHttp(~)
为指定的 URI 字符串初始化新的 HttpWebRequest 实例
GetRequestStream()
返回用于将(请求)数据写入 Internet 资源的 Stream
GetResponse()
返回对 Internet 请求的响应的 WebResponse
主要派生类
PackWebRequest
向整个 PackagePart 或包中的 PackagePart 发出请求,由 Pack URI 标识
FileWebRequest
提供 WebRequest 类的文件系统实现
FtpWebRequest
实现文件传输协议 (FTP) 客户端
HttpWebRequest
提供 WebRequest 类的 HTTP 特定的实现
主要属性
CookieContainer
与此请求关联的 Cookie
WebResponse
提供来自统一资源标识符 (URI) 的响应
主要属性
ResponseUri
实际响应此请求的 Internet 资源的 URI
Headers
与此请求关联的标头名称/值对的集合
ContentType
接收的数据的内容类型
ContentLength
接收的数据的内容长度
主要方法
GetResponseStream()
返回从 Internet 资源中读取(响应)数据的 Stream 类的实例
Close()
关闭响应(流)
主要派生类
HttpWebResponse
提供 WebResponse 类的 HTTP 特定的实现
主要属性
Cookies
此响应关联的 Cookie
WebClient
提供用于将数据发送到由 URI 标识的资源及从这样的资源接收数据的常用方法
主要属性
BaseAddress
基 URI
指定该属性后,其他的某些方法(如 DownloadFile)参数 uri 只需要指定 uriSuffix(后缀部分)。 例如 "http://www.contoso.com/default.htm" 中的 "default.htm"。
Credentials
Encoding
用于上传和下载字符串的 Encoding
Headers
Proxy
ResponseHeaders
主要方法
DownloadString(~)
此方法使用 RETR 命令下载 FTP 资源。 对于 HTTP 资源,使用 GET 方法。
DownloadFile(~)
DownloadData(~)
OpenRead(~)
UploadString(~)
此方法使用 STOR 命令上传 FTP 资源。 对于 HTTP 资源,将使用 POST 方法。
UploadFile(~)
UploadData(~)
OpenWrite(~)
HttpListener
提供一个简单的、可通过编程方式控制的 HTTP 协议侦听器
ServicePoint
提供 HTTP 连接的连接管理
ServicePointManager
管理 ServicePoint 对象集合
WebUtility
提供用于在处理 Web 请求时编码和解码 URL 的方法
类似功能的类:System.Web.HttpUtility
一般用于服务端。
System.Net.Cache
定义一些类型和枚举,这些类型和枚举用于为通过 System.Net.WebRequest 和 System.Net.HttpWebRequest 类获取的资源制定缓存策略。
System.Net.Configuration
应用程序以编程方式访问和更新 System.Net 命名空间的配置设置时所使用的类。
System.Net.Http
用于为现代 HTTP 应用程序提供编程接口的类。
主要类
HttpClient
用于发送 HTTP 请求并从 URI 标识的资源接收 HTTP 响应
System.Net.Http.Headers
为 System.Net.Http 命名空间使用的 HTTP 标头集合提供支持。
System.Net.Mail
使用 SMTP 协议撰写和发送邮件时所使用的类。
System.Net.Mime
定义一些类型,这些类型用于表示供 System.Net.Mail 命名空间中的类所使用的多用途 Internet 邮件交换 (MIME) 标头。
System.Net.NetworkInformation
用于以编程方式收集有关网络事件、更改、统计信息和属性的信息的类。
System.Net.PeerToPeer
为开发人员提供对等名称解析协议 (PNRP) 的一种托管实现。
System.Net.PeerToPeer.Collaboration
为开发人员提供对接协作接口的一种托管实现。
System.Net.Security
用于为主机间的安全通信提供网络流的类。
System.Net.Sockets
为需要帮助控制网络访问的开发人员提供 Windows 套接字 (Winsock) 接口的一种托管实现。
主要类
Socket
实现 Berkeley 套接字接口
TcpClient
为 TCP 网络服务提供客户端连接
TcpListener
侦听来自 TCP 网络客户端的连接
UdpClient
提供用户数据报协议 (UDP) 网络服务
System.Net.WebSockets
为开发人员提供 WebSocket 接口的一种托管实现。
System.Uri 类
提供统一资源标识符 (URI) 的对象表示形式和对 URI 各部分的轻松访问。
System.Security.Authentication.ExtendedProtection
为采用应用程序扩展保护的身份验证提供支持。
System.Security.Authentication.ExtendedProtection.Configuration
为配置采用应用程序扩展保护的身份验证提供支持。
IPGlobalProperties 类
提供有关本地计算机的网络连接的信息
方法
GetActiveTcpConnections
关于 (TCP) 连接
GetActiveTcpListeners
关于 (TCP) 侦听器的终结点
GetActiveUdpListeners
关于 (UDP) 侦听器
NetworkInterface 类
提供网络接口的配置和统计信息
命名空间:System.Net.NetworkInformation
System.Web.HttpUtility 类
提供在处理 Web 请求时用于编码和解码 URL 的方法。
System.Net.CookieContainer 类
public CookieContainer(int capacity, int perDomainCapacity, int maxCookieSize)
capacity
System.Net.Cookie 可以包含的 System.Net.CookieContainer 实例数
perDomainCapacity
每个域的 System.Net.Cookie 实例数
值 ≤ capacity
maxCookieSize
System.Net.Cookie 中任何单个 System.Net.CookieContainer 的最大大小(以字节为单位)
相关示例
TCP协议
例
封装的Socket方式
客户端
using System; using System.Collections.Generic; using System.Text; using System.Net.Sockets; namespace Client { class Program { static void Main(string[] args) { TcpClient client = null; try { Console.WriteLine("连接服务器..."); //1、初始化 TcpClient 类的新实例并连接到指定主机上的指定端口 client = new TcpClient("127.0.0.1", 13000); if (client.Connected) { Console.WriteLine("已连接!"); //2、获取用于发送和接收数据的 NetworkStream using (NetworkStream stream = client.GetStream()) { while (true) { Console.Write("输入>"); string sendMsg = Console.ReadLine(); byte[] sendData = Encoding.UTF8.GetBytes(sendMsg); //数据写入流 stream.Write(sendData, 0, sendData.Length); Console.WriteLine($"发:{sendMsg}"); List<byte> readData = new List<byte>(); byte[] readBuffer = new byte[256]; do { //从流读数据 int count = stream.Read(readBuffer, 0, readBuffer.Length); if (count < readBuffer.Length) { byte[] newBuffer = new byte[count]; Buffer.BlockCopy(readBuffer, 0, newBuffer, 0, count); readBuffer = newBuffer; } readData.AddRange(readBuffer); } while (stream.DataAvailable); //流上是否有可用的数据 string readMsg = Encoding.UTF8.GetString(readData.ToArray()); Console.WriteLine($"收:{readMsg}"); if (sendMsg.Equals("exit", StringComparison.OrdinalIgnoreCase)) { break; } } } } } catch (Exception ex) { Console.WriteLine($"异常:{ex.Message}"); } finally { if (null != client) { //3、释放此 TcpClient 实例,并请求关闭基础 TCP 连接 client.Close(); } } Console.WriteLine("按任意键退出..."); Console.Read(); } } }
1、初始化 TcpClient 类的新实例并连接到指定主机上的指定端口
client = new TcpClient("127.0.0.1", 13000);
是否连接
if (client.Connected)
2、获取用于发送和接收数据的 NetworkStream
using (NetworkStream stream = client.GetStream())
数据写入流
stream.Write(sendData, 0, sendData.Length);
从流读数据
int count = stream.Read(readBuffer, 0, readBuffer.Length);
流上是否有可用的数据
} while (stream.DataAvailable);
3、释放此 TcpClient 实例,并请求关闭基础 TCP 连接
client.Close();
服务器端
using System; using System.Collections.Generic; using System.Text; using System.Threading; using System.Net; using System.Net.Sockets; namespace Server { class Program { static void Main(string[] args) { TcpListener server = null; try { //1、初始化 TcpListener 类的新实例,该类在指定的本地 IP 地址和端口号上侦听是否有传入的连接尝试 server = new TcpListener(IPAddress.Parse("127.0.0.1"), 13000); //2、开始侦听传入的连接请求 server.Start(); Timer timer = new Timer(CheckConnection, server, 0, 1000); while (true) { Thread.Sleep(10000); } } catch (Exception ex) { Console.WriteLine($"侦听器异常:{ex.Message}"); } finally { if (null != server) { //3、关闭侦听器 server.Stop(); } } Console.WriteLine("按任意键退出..."); Console.Read(); } static void CheckConnection(object obj) { try { TcpListener server = obj as TcpListener; //2.1、是否有挂起的连接请求 if (server.Pending()) { Console.WriteLine("检测到客户端连接..."); ThreadPool.QueueUserWorkItem(ConnectionHandle, server); } } catch (Exception ex) { Console.WriteLine($"检测客户端连接过程异常:{ex.Message}"); } } static void ConnectionHandle(object obj) { TcpListener server = obj as TcpListener; TcpClient client = null; try { Console.WriteLine($"接受客户端连接... 处理线程:{Thread.CurrentThread.ManagedThreadId}"); //2.1.1、接受挂起的连接请求,返回用于发送和接收数据的 TcpClient。(该方法为阻塞方法) client = server.AcceptTcpClient(); if (client.Connected) { Console.WriteLine("已连接!"); //2.1.2、获取用于发送和接收数据的 NetworkStream using (NetworkStream stream = client.GetStream()) { while (true) { List<byte> readData = new List<byte>(); byte[] readBuffer = new byte[256]; do { //从流读数据 int count = stream.Read(readBuffer, 0, readBuffer.Length); if (count < readBuffer.Length) { byte[] newBuffer = new byte[count]; Buffer.BlockCopy(readBuffer, 0, newBuffer, 0, count); readBuffer = newBuffer; } readData.AddRange(readBuffer); } while (stream.DataAvailable); //流上是否有可用的数据 string readMsg = Encoding.UTF8.GetString(readData.ToArray()); Console.WriteLine($"收:{readMsg} 处理线程:{Thread.CurrentThread.ManagedThreadId}"); string sendMsg = readMsg.ToUpper(); byte[] sendData = Encoding.UTF8.GetBytes(sendMsg); //数据写入流 stream.Write(sendData, 0, sendData.Length); Console.WriteLine($"发:{sendMsg} 处理线程:{Thread.CurrentThread.ManagedThreadId}"); if (readMsg.Equals("exit", StringComparison.OrdinalIgnoreCase)) { break; } } } } } catch (Exception ex) { Console.WriteLine($"连接异常:{ex.Message} 处理线程:{Thread.CurrentThread.ManagedThreadId}"); } finally { if (null != client) { //2.1.3、释放此 TcpClient 实例,并请求关闭基础 TCP 连接 client.Close(); } Console.WriteLine($"断开客户端连接。 处理线程:{Thread.CurrentThread.ManagedThreadId}"); } } } }
1、初始化 TcpListener 类的新实例,该类在指定的本地 IP 地址和端口号上侦听是否有传入的连接尝试
server = new TcpListener(IPAddress.Parse("127.0.0.1"), 13000);
2、开始侦听传入的连接请求
server.Start();
2.1、是否有挂起的连接请求
if (server.Pending())
2.1.1、接受挂起的连接请求,返回用于发送和接收数据的 TcpClient。(该方法为阻塞方法)
client = server.AcceptTcpClient();
是否连接
if (client.Connected)
2.1.2、获取用于发送和接收数据的 NetworkStream
using (NetworkStream stream = client.GetStream())
数据写入流
stream.Write(sendData, 0, sendData.Length);
从流读数据
int count = stream.Read(readBuffer, 0, readBuffer.Length);
流上是否有可用的数据
} while (stream.DataAvailable);
2.1.3、释放此 TcpClient 实例,并请求关闭基础 TCP 连接
client.Close();
3、关闭侦听器
server.Stop();
Socket方式
客户端
using System; using System.Collections.Generic; using System.Text; using System.Net.Sockets; namespace Client { class Program { static void Main(string[] args) { Socket socket = null; try { Console.WriteLine("连接服务器..."); socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //初始化 Socket 类的新实例 socket.Connect("127.0.0.1", 13000); //建立与远程主机的连接 if (socket.Connected) { Console.WriteLine("已连接!"); while (true) { Console.Write("输入>"); string sendMsg = Console.ReadLine(); byte[] sendData = Encoding.UTF8.GetBytes(sendMsg); socket.Send(sendData); //发送数据 Console.WriteLine($"发:{sendMsg}"); List<byte> readData = new List<byte>(); byte[] readBuffer = new byte[256]; do { int count = socket.Receive(readBuffer); //接收数据 if (count < readBuffer.Length) { byte[] newBuffer = new byte[count]; Buffer.BlockCopy(readBuffer, 0, newBuffer, 0, count); readBuffer = newBuffer; } readData.AddRange(readBuffer); } while (socket.Available > 0); //是否还有可用的数据 string readMsg = Encoding.UTF8.GetString(readData.ToArray()); Console.WriteLine($"收:{readMsg}"); if (sendMsg.Equals("exit", StringComparison.OrdinalIgnoreCase)) { break; } } } } catch (Exception ex) { Console.WriteLine($"异常:{ex.Message}"); } finally { if (null != socket) { socket.Close(); //关闭 Socket 连接并释放所有关联的资源 } } Console.WriteLine("按任意键退出..."); Console.Read(); } } }
服务器端
using System; using System.Collections.Generic; using System.Text; using System.Threading; using System.Net; using System.Net.Sockets; namespace Server { class Program { static void Main(string[] args) { Socket socket = null; try { socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //初始化 Socket 类的新实例 socket.Bind(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 13000)); //使 Socket 与一个本地终结点相关联 socket.Listen(int.MaxValue); //将 Socket 置于侦听状态 Timer timer = new Timer(CheckConnection, socket, 0, 1000); while (true) { Thread.Sleep(10000); } } catch (Exception ex) { Console.WriteLine($"侦听器异常:{ex.Message}"); } finally { if (null != socket) { socket.Close(); //关闭 Socket 连接并释放所有关联的资源 } } Console.WriteLine("按任意键退出..."); Console.Read(); } static void CheckConnection(object obj) { try { Socket socket = obj as Socket; if (socket.Poll(0, SelectMode.SelectRead)) //是否有挂起的连接请求 { Console.WriteLine("检测到客户端连接..."); ThreadPool.QueueUserWorkItem(ConnectionHandle, socket); } } catch (Exception ex) { Console.WriteLine($"检测客户端连接过程异常:{ex.Message}"); } } static void ConnectionHandle(object obj) { Socket socket = obj as Socket; Socket client = null; try { Console.WriteLine($"接受客户端连接... 处理线程:{Thread.CurrentThread.ManagedThreadId}"); client = socket.Accept(); //以同步方式从侦听套接字的连接请求队列中提取第一个挂起的连接请求,然后创建并返回新的 Socket。(该方法为阻塞方法) if (client.Connected) { Console.WriteLine("已连接!"); while (true) { List<byte> readData = new List<byte>(); byte[] readBuffer = new byte[256]; do { int count = client.Receive(readBuffer); //接收数据 if (count < readBuffer.Length) { byte[] newBuffer = new byte[count]; Buffer.BlockCopy(readBuffer, 0, newBuffer, 0, count); readBuffer = newBuffer; } readData.AddRange(readBuffer); } while (client.Available > 0); //是否还有可用的数据 string readMsg = Encoding.UTF8.GetString(readData.ToArray()); Console.WriteLine($"收:{readMsg} 处理线程:{Thread.CurrentThread.ManagedThreadId}"); string sendMsg = readMsg.ToUpper(); byte[] sendData = Encoding.UTF8.GetBytes(sendMsg); client.Send(sendData); //发送数据 Console.WriteLine($"发:{sendMsg} 处理线程:{Thread.CurrentThread.ManagedThreadId}"); if (readMsg.Equals("exit", StringComparison.OrdinalIgnoreCase)) { break; } } } client.Shutdown(SocketShutdown.Both); //禁用 Socket 上的发送和接收 } catch (Exception ex) { Console.WriteLine($"连接异常:{ex.Message} 处理线程:{Thread.CurrentThread.ManagedThreadId}"); } finally { if (null != client) { client.Close(); //关闭 Socket 连接并释放所有关联的资源 } Console.WriteLine($"断开客户端连接。 处理线程:{Thread.CurrentThread.ManagedThreadId}"); } } } }
UDP协议
例
封装的Socket方式
终端1
using System; using System.Text; using System.Net; using System.Net.Sockets; namespace Terminal { class Program { static void Main(string[] args) { IPEndPoint localHost = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 13000); IPEndPoint remoteHost = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 13001); UdpClient terminal = null; try { Console.WriteLine("初始化..."); terminal = new UdpClient(localHost); //初始化 UdpClient 类的新实例,并将其绑定到指定的本地终结点 Console.WriteLine("完成!"); while (true) { Console.Write("输入>"); string sendMsg = Console.ReadLine(); byte[] sendData = Encoding.UTF8.GetBytes(sendMsg); terminal.Send(sendData, sendData.Length, remoteHost); //将 UDP 数据报发送到远程主机 Console.WriteLine($"发:{sendMsg}"); byte[] readBuffer = terminal.Receive(ref remoteHost); //接收远程主机发送的 UDP 数据报 string readMsg = Encoding.UTF8.GetString(readBuffer); Console.WriteLine($"收:{readMsg}"); if (sendMsg.Equals("exit", StringComparison.OrdinalIgnoreCase)) { break; } } } catch (Exception ex) { Console.WriteLine($"异常:{ex.Message}"); } finally { if (null != terminal) { terminal.Close(); //关闭 UDP 连接 } } Console.WriteLine("按任意键退出..."); Console.Read(); } } }
初始化 UdpClient 类的新实例,并将其绑定到指定的本地终结点
terminal = new UdpClient(localHost);
将 UDP 数据报发送到远程主机
terminal.Send(sendData, sendData.Length, remoteHost);
接收远程主机发送的 UDP 数据报
byte[] readBuffer = terminal.Receive(ref remoteHost);
关闭 UDP 连接
terminal.Close();
终端2
using System; using System.Text; using System.Net; using System.Net.Sockets; namespace Terminal { class Program { static void Main(string[] args) { IPEndPoint localHost = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 13001); IPEndPoint remoteHost = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 13000); UdpClient terminal = null; try { Console.WriteLine("初始化..."); terminal = new UdpClient(localHost); //初始化 UdpClient 类的新实例,并将其绑定到指定的本地终结点 Console.WriteLine("完成!"); while (true) { byte[] readBuffer = terminal.Receive(ref remoteHost); //接收远程主机发送的 UDP 数据报 string readMsg = Encoding.UTF8.GetString(readBuffer); Console.WriteLine($"收:{readMsg}"); string sendMsg = readMsg.ToUpper(); byte[] sendData = Encoding.UTF8.GetBytes(sendMsg); terminal.Send(sendData, sendData.Length, remoteHost); //将 UDP 数据报发送到远程主机 Console.WriteLine($"发:{sendMsg}"); if (readMsg.Equals("exit", StringComparison.OrdinalIgnoreCase)) { break; } } } catch (Exception ex) { Console.WriteLine($"异常:{ex.Message}"); } finally { if (null != terminal) { terminal.Close(); //关闭 UDP 连接 } } Console.WriteLine("按任意键退出..."); Console.Read(); } } }
HTTP请求
例
HttpWebRequest请求
客户端
using System; using System.Net; using System.IO; namespace Client { class Program { static void Main(string[] args) { HttpWebRequest httpWebRequest; HttpWebResponse httpWebResponse = null; string responseContent; try { httpWebRequest = WebRequest.CreateHttp("https://www.baidu.com/"); //1、为指定的 URI 字符串初始化新的 HttpWebRequest 实例 httpWebResponse = httpWebRequest.GetResponse() as HttpWebResponse; //2、获取对互联网请求的响应的 WebResponse using (Stream responseStream = httpWebResponse.GetResponseStream()) //3、获取从互联网资源中读取(响应)数据的 Stream 类的实例 { //如果响应数据流被压缩,则需要先根据压缩方式解压缩,再使用解压缩后的数据流进行后续操作。如: //System.IO.Compression.GZipStream responseStreamDecompress = new System.IO.Compression.GZipStream(responseStream, System.IO.Compression.CompressionMode.Decompress); using (StreamReader streamReader = new StreamReader(responseStream)) { responseContent = streamReader.ReadToEnd(); } } Console.WriteLine(responseContent); } catch (WebException webEx) { Console.WriteLine($"请求出现 WebException 异常:{webEx.Message}"); Console.WriteLine($"请求响应状态:{webEx.Status}"); HttpWebResponse httpWebResponseEx = null; try { httpWebResponseEx = webEx.Response as HttpWebResponse; string contentEx; using (Stream responseStreamEx = httpWebResponseEx.GetResponseStream()) { using (StreamReader streamReaderEx = new StreamReader(responseStreamEx)) { contentEx = streamReaderEx.ReadToEnd(); } } Console.WriteLine($"异常响应状态码:{(int)httpWebResponseEx.StatusCode} {httpWebResponseEx.StatusCode}"); Console.WriteLine($"异常响应状态描述:{httpWebResponseEx.StatusDescription}"); Console.WriteLine($"异常响应数据:{contentEx}"); } catch (Exception ex) { Console.WriteLine($"处理异常响应异常:{ex.Message}"); } finally { if (httpWebResponseEx != null) { httpWebResponseEx.Close(); } } } catch (Exception ex) { Console.WriteLine($"请求出现异常:{ex.Message}"); } finally { if (httpWebResponse != null) { httpWebResponse.Close(); //4、关闭响应(流) } } Console.WriteLine("按任意键退出..."); Console.Read(); } } }
第三方 RestSharp
可使用 NuGet 安装到工程。
客户端
using System; using RestSharp; namespace Client { class Program { static void Main(string[] args) { try { RestClient restClient = new RestClient("https://www.baidu.com"); //初始化 RestClient 客户端实例 RestRequest restRequest = new RestRequest(Method.GET); //初始化请求 RestResponse restResponse = restClient.Execute(restRequest) as RestResponse; //执行请求并获取响应 Console.WriteLine(restResponse.Content); } catch (Exception ex) { Console.WriteLine($"请求出现异常:{ex.Message}"); } finally { } Console.WriteLine("按任意键退出..."); Console.Read(); } } }
安全
命名空间:System.Security.Cryptography
CMD查看哈希
CMD: 查看MD5值 certutil -hashfile 文件名 MD5 查看 SHA1 certutil -hashfile 文件名 SHA1 查看SHA256 certutil -hashfile 文件名 SHA256
MD5 类
MD5CryptoServiceProvider
计算文件MD5
例: using System; using System.IO; using System.Security.Cryptography; namespace ConsoleApp1 { class Program { static void Main(string[] args) { string filePath = @""; MD5CryptoServiceProvider md5Provider = new MD5CryptoServiceProvider(); using (Stream fs = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.Read)) { byte[] md5Hs = md5Provider.ComputeHash(fs); string md5 = BitConverter.ToString(md5Hs); md5 = md5.Replace("-", String.Empty).ToLower(); } } } }
RNGCryptoServiceProvider 类
使用加密服务提供程序 (CSP) 提供的实现来实现加密随机数生成器 (RNG)
例:s
using System.Security.Cryptography; namespace Client { class Program { static void Main(string[] args) { byte[] rdbytes = new byte[4]; //字节数组的长度决定了生成多少个加密强随机字节 RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider(); rng.GetBytes(rdbytes); rng.Dispose(); } } }
HMACSHA256
绘图
命名空间
System.Drawing
GDI 基本功能
Graphical Device Interface 图形设备接口
System.Drawing.Drawing2D
高级的 2D 和向量图形
System.Drawing.Imaging
高级的图像处理
System.Drawing.Text
高级的文本显示
System.Drawing.Printing
打印功能
注意
图形对象都要注意 Dispose() 方法的调用,及时释放资源
位置和大小
结构体
Point、PointF、Rectangle、RectangleF、Size、SizeF
System.Drawing
颜色
Color 结构体
System.Drawing
主要成员
属性
A、R、G、B
预定义标准颜色
方法
FromArgb、FromKnownColor
KnownColor 枚举
System.Drawing
已知预定义、系统颜色枚举
SystemColors 类
预定义系统颜色
ColorTranslator 类
System.Drawing
使用
//例: string colorStr = "#12345678"; Color color = ColorTranslator.FromHtml(colorStr); //A 0x12 R 0x34 G 0x56 B 0x78 colorStr = ColorTranslator.ToHtml(color); //结果 #345678 colorStr = ColorTranslator.ToHtml(Color.Blue); //结果 Blue
画笔
Pen 类
System.Drawing
用于绘制轮廓
主要成员
属性
Alignment 获取(或设置)这只画笔所绘制对象的对齐 Brush 获取(或设置)与这只画笔相关的刷子 Color 获取(或设置)这只画笔的颜色 DashPattern 获取(或设置)自定义的破折号和空格的排列 DashStyle 表示这条线所使用的破折号样式(点划线样式) LineJoin 表示线条连接的方法 MiterLimit 表示在斜接角上,连接厚度的限度 PenType 说明画笔的类型 StartCap,EndCap 表示线条的开始罩(cap)和结束罩 Transform 一个矩阵,用于描述该画笔所绘制对象是如何转换的 Width 获取(或设置)画笔的像素宽度
绘制后,需手动 Dispose() (自己创建的?)实例
Pens 类
预定义标准颜色画笔
SystemPens 类
预定义系统颜色画笔
画刷
Brush 类
System.Drawing
用于填充区域
派生类
SolidBrush
System.Drawing
单色画刷
主要成员
属性
Color 刷子颜色
TextureBrush
System.Drawing
图像画刷
主要成员
属性
Image 画刷图像
LinearGradientBrush
System.Drawing.Drawing2D
渐变画刷
PathGradientBrush
System.Drawing.Drawing2D
路径渐变画刷
例:
GraphicsPath path = new GraphicsPath(); path.AddEllipse(0, 0, 140, 70); PathGradientBrush pthGrBrush = new PathGradientBrush(path); pthGrBrush.CenterColor = Color.FromArgb(255, 0, 0, 255); Color[] colors = { Color.FromArgb(255, 0, 255, 255) }; pthGrBrush.SurroundColors = colors; e.Graphics.FillEllipse(pthGrBrush, 0, 0, 140, 70);
HatchBrush
System.Drawing.Drawing2D
图案画刷
绘制后,需手动 Dispose() (自己创建的?)实例
Brushes 类
预定义标准颜色画刷
SystemBrushes 类
预定义系统颜色画刷
绘图
坐标系
Device(设备)
即物理显示设备
单位
一般为 像素
Page(页)
绘图图面的坐标,如控件、窗体
坐标原点始终为可视区域左上角
单位
由 PageUnit 属性定义
World(世界)
建模坐标,即传递给方法的坐标
其他
屏幕坐标、全窗口坐标、客户区坐标
视口(设备)坐标、窗口(逻辑)坐标
类
Graphics
设备独立的绘图基类
主要成员
方法
绘图方法
Clear 用给定颜色填充绘图区域 DrawArc 从一个椭圆绘制弧线 DrawBezier, DrawBeziers 绘制一条或多条贝塞尔曲线 DrawCurve, DrawClosedCurve 绘制开曲线或闭曲线(这些曲线由一个点数组表示) DrawEllipse 绘制一个椭圆 Drawlcon 绘制一个图标 DrawImage 绘制一个图像 DrawLine, DrawLines 绘制一条或多条线 DrawPie 绘制圆形分格统计图表 DrawPolygon 绘制多边形 DrawRectangle, DrawRectangles 绘制一个或多个矩形 DrawString 绘制一个字符串 FillClosedCurve 填充封闭曲线(这个曲线由一个点数组表示) FillEllipse 绘制一个填充椭圆 FillPie 绘制一个填充的圆形分格统计图表 FillPolygon 绘制一个填充的多边形 FillRectangle, FillRectangles 绘制一个或多个填充的矩形
非绘图方法
Dispose 释放图形对象使用的Windows资源 Finalize 当图形对象被放到回收站时调用 Flush 强制立即执行队列中的所有图形命令 FromHdc 从 WindowsHDC 句柄中创建一个图形对象 FromHwnd 从 WindowsHWND 中创建一个图形对象(HWND:Control.Handle) FromImage 从一个图像对象中创建一个图形对象 GetHdc 返回一个表示图形对象的 WindowsHDC(使用后需 ReleaseHdc) GetNearestColor 获取与已知颜色最接近的颜色 IsVisible 表示一个点或一个矩形是否在图形对象的剪切区域内 MeasureString 返回字符串(这个字符串通过给定的字体被绘制)的大小 SetClip,ResetClip 设置或重新设置图形对象的剪切区域 ResetTransform 重新设置与图形对象相关的图形转换 RotateTransform 给图形对象添加一个图形的旋转转换 Save, Restore 保存或者恢复图形对象的状态 ScaleTransform 给图形对象添加一个图形的缩放转换 TransformPoints 将当前转换用于转换一个点数组 TranslateTransform 给图形对象添加一个图形的平移转换
属性
Clip 获取或设置 Region,该对象限定此 Graphics 的绘图区域; ClipBounds 获取一个 RectangleF 结构,该结构限定此 Graphics 的剪辑区域; CompositingMode 获取一个值,该值指定如何将合成图像绘制到此 Graphics;决定图形像素是覆盖(CompositingMode.SourceCopy), 还是和背景像素合成(CompositingMode.SourceOver); CompositingQuality 获取或设置绘制到此 Graphics 的合成图像的呈现质量; DpiX 获取此 Graphics 的水平分辨率; DpiY 获取此 Graphics 的垂直分辨率; InterpolationMode 获取或设置与此 Graphics 关联的插补模式(两点间数据如何被截取); IsClipEmpty 获取一个值,该值指示此 Graphics 的剪辑区域是否为空; IsVisibleClipEmpty 获取一个值,该值指示此 Graphics 的可见剪辑区域是否为空; PageScale 获取或设置此 Graphics 的世界单位和页单位之间的比例; PageUnit 获取或设置用于此 Graphics 中的页坐标的度量单位; PixelOffsetMode 获取或设置一个值,该值指定在呈现此 Graphics 的过程中像素如何偏移; RenderingOrigin 为抵色处理和阴影画笔获取或设置此 Graphics 的呈现原点; SmoothingMode 获取或设置此 Graphics 的呈现质量;着色质量(文本平滑)(默认情况下是:反向替换、高速,高质量); TextContrast 获取或设置呈现文本的灰度校正值; TextRenderingHint 获取或设置与此 Graphics 关联的文本的呈现模式; Transform 获取或设置此 Graphics 的几何世界变换的副本;当前的图形转换(即 World -> Page 的映射关系); VisibleClipBounds 获取此 Graphics 的可见剪辑区域的边框;
关于 PageUnit 属性
类型:GraphicsUnit 枚举: Display 指定显示设备的度量单位。 通常,视频显示使用的单位是像素;打印机使用的单位是 1/100 英寸。 Document 将文档单位(1/300 英寸)指定为度量单位。 Inch 将英寸指定为度量单位。 Millimeter 将毫米指定为度量单位。 Pixel 将设备像素指定为度量单位。 Point 将打印机点(1/72 英寸)指定为度量单位。 World 将世界坐标系单位指定为度量单位。
实例获取
例:
从窗体、控件的重绘事件获取
绘制后,无需手动 Dispose() 实例,调用者会自动处理
例:
基础使用
//通过 Paint 事件,或重写 OnPaint() 方法使用: private void Form1_Paint(object sender, PaintEventArgs e) { Graphics g = e.Graphics; //事件的 Graphics 属性表示用于绘制的图面对象 g.FillEllipse(Brushes.Blue, e.ClipRectangle); //事件的 ClipRectangle 属性指定重绘区域 g.DrawString("Text", SystemFonts.DefaultFont, Brushes.Black, 0, 0); }
双缓存绘图
自动实现: 窗体最好设置从 Control 类继承的 DoubleBuffered 属性为 true,这样绘图时,会先绘制到缓存的内存,再绘制到屏幕,以减少绘图时的闪烁。
手动实现
1.创建一个 Image 对象; 2.由 Image 对象产生一个缓存 Graphics 对象(FromImage 方法); 3.在缓存 Graphics 对象上绘图; 4.在目标 Graphics 对象上绘制出上述 Image 对象(DrawImage 方法);
窗体滚动时,偏移绘制图面(坐标)
private void Form1_Paint(object sender, PaintEventArgs e) { e.Graphics.TranslateTransform(this.AutoScrollPosition.X, this.AutoScrollPosition.Y); //根据滚动位置,平移绘图图面。实现图面坐标原点“效果上”与滚动位置一致,而不是固定在可视区域左上角 e.Graphics.DrawString("666", SystemFonts.DefaultFont, Brushes.Black, 100, 200); //经过上述平移,此时,绘制位置与期望一致 }
借助窗体、控件生成
绘制后,需手动 Dispose() 实例
例:
Graphics g = button1.CreateGraphics(); //使用控件的 CreateGraphics() 方法创建 g.DrawString("123", SystemFonts.DefaultFont, Brushes.Black, 10, 100); g.Dispose();
从图像生成
绘制后,需手动 Dispose() 实例
例:
Bitmap bitmap = new Bitmap("test.jpg"); Graphics g = Graphics.FromImage(bitmap); //从图像创建 g.DrawString("123", SystemFonts.DefaultFont, Brushes.Red, 10, 50); g.Dispose(); bitmap.Save("test2.jpg"); bitmap.Dispose();
从句柄生成
绘制后,需手动 Dispose() 实例
例:
//方式1: Graphics g = Graphics.FromHwnd(button1.Handle); //使用控件的句柄创建 g.DrawString("456", SystemFonts.DefaultFont, Brushes.Black, 10, 200); g.Dispose(); //方式2: HandleRef handleRef = new HandleRef(button1, button1.Handle); Graphics g = Graphics.FromHwnd(handleRef.Handle); //使用控件的句柄创建 g.DrawString("456", SystemFonts.DefaultFont, Brushes.Black, 10, 200); g.Dispose();
坐标变换
缩放变换
用指定的 X轴和Y轴 比例 对图面进行缩放
使用 ScaleTransform 方法
例: Graphics g = e.Graphics; g.ScaleTransform(1.5f, 1.5f); g.ScaleTransform(1.5f, 1.5f, MatrixOrder.Prepend); g.ScaleTransform(1.5f, 1.5f, MatrixOrder.Append);
旋转变换
相对于 图面原点 旋转指定的角度
使用 RotateTransform 方法
例: Graphics g = e.Graphics; g.RotateTransform(10); //度 g.RotateTransform(10, MatrixOrder.Prepend); g.RotateTransform(10, MatrixOrder.Append);
平移变换
用指定的 X轴和Y轴 偏移量 对图面进行平移
使用 TranslateTransform 方法
例: Graphics g = e.Graphics; g.TranslateTransform(10, 20); g.TranslateTransform(10, 20, MatrixOrder.Prepend); //Prepend 基于原坐标系变换 g.TranslateTransform(10, 20, MatrixOrder.Append); //Append 基于变换后的坐标系变换
变换矩阵
用指定的 变换矩阵 对图面进行变换
设置 Transform 属性
关于“仿射变换”
是指几何中,对一个向量空间进行线性变换并接上一个平移,变换为另一个向量空间
表示仿射变换的矩阵即“仿射矩阵”(第3列始终为(0, 0, 1))
(x, y) 写成 [x, y, 1] 使其可与仿射矩阵相乘; [x, y, 1] 是变换前的坐标; [x', y', 1] 是变换后的坐标; 矩阵乘法: 一个矩阵的行数、和另一个矩阵的列数要相同。
步骤:对1个点先缩放(算s部分,其他看为0)、再旋转(算r部分,其他看为0)、后平移(算d部分,其他看为0)
例:
Graphics g = e.Graphics; float sx = 1, sy = 2; float rx = 1, ry = -1; float dx = 0, dy = 50; g.Transform = new Matrix(sx, ry, rx, sy, dx, dy); g.DrawString("666666", SystemFonts.DefaultFont, Brushes.Black, new PointF(0, 0));
图面刷新方法
Invalidate, Update, 和 Refresh
1、Invalidate将窗口标记为无效,以便下次处理事件时重新绘制。也就是说Invalidate不是同步的; 2、如果控件的更新区域不是空的,则Update会向控件发送WM_PAINT消息; 3、Refresh调用Invalidate和Update以同步刷新;
字体
Font 类
System.Drawing
表示字体
字体默认大小
磅值
磅即 Point、pt,点。(注意不是像素点)
1磅为1/72英寸
主要成员
属性
Bold 如果字体是粗体,返回Tnue(只读) FontFamily 返回字体关联的FontFamily对象(只读) Height 返回字体的高度(只读) Italic 如果字体是斜体,返回True(只读) Name 返回字体的名称(只读) Size 返回字体大小(只读) SizeInPoints 返回字体大小的磅值大小(只读) Strikeout 如果字体是突出(struck-out)的,则返回True(只读) Style 返回一个描述字体样式的FontStyle对象(只读) Underline 如果字体有下划线,则返回Tue(只读) Unit 返回字体的图形单位(只读) FontStyle 枚举: Bold 描述粗体文本; Italic 描述斜体文本; Regular 描述常规文本; Strikeout 描述带有线条横穿的文本; Underline 描述带下划线的文本;
方法
Clone 创建一个字体对象的准确拷贝 Dispose 释放字体使用的Windows资源 FromHde 从WindowsHDC中创建一个字体 GetHeight 获取在一个具体Graphics上下文中的字体高度 ToHfont,FromHfont 从WindowsHFONT转换或者转换成WindowsHFONT ToLogFont,FromLogFont 从WindowsLOGFONT结构转换或者转换成WindowsLOGFONT结构
创建字体
提供字体家族、大小、样式等创建
例:
Font font = new Font("黑体", 12, FontStyle.Regular);
FontFamily 类
System.Drawing
表示具有相似字体但可能具有不同大小和样式的一组字体(字体家族)
SystemFonts 类
预定义系统字体
获取可用字体(家族)集合
获取已安装的字体
使用 InstalledFontCollection 类
例:
//创建一个实例获取 (new System.Drawing.Text.InstalledFontCollection()).Families
获取可用于绘图的字体
使用 FontFamily 类
例:
//使用静态属性 System.Drawing.FontFamily.Families
图形轮廓路径
使用 GraphicsPath 类
System.Drawing.Drawing2D
关于 FillMode 填充模式
Alternate(交替填充,默认)

射线穿过奇条边框线时,则填充该边框线区域
穿过偶数条边框线时,则不填充
Winding(环绕填充)

射线穿过奇条边框线时,则填充该边框线区域,
穿过偶数条边框线时, 该区域边框线绘制方向可以环绕成一圈的,则填充;不能环成一圈则不填充
例:
绘制字符串轮廓
Graphics g = e.Graphics; GraphicsPath gp = new GraphicsPath(FillMode.Winding); gp.AddString("AaBbCc123456绘制字体", FontFamily.Families.FirstOrDefault(), (int)FontStyle.Bold, 50, new Point(10, 10), new StringFormat()); //为路径添加字符串路径 g.DrawPath(Pens.Blue, gp); //绘制轮廓 g.FillPath(Brushes.Red, gp); //填充轮廓内图形
屏幕
例:
截屏
var primaryScreen = System.Windows.Forms.Screen.PrimaryScreen; var bitmap = new System.Drawing.Bitmap(primaryScreen.Bounds.Size.Width, primaryScreen.Bounds.Size.Height); Graphics g = Graphics.FromImage(bitmap); g.CopyFromScreen(0, 0, 0, 0, primaryScreen.Bounds.Size); //截屏 g.PageUnit = GraphicsUnit.Millimeter; g.DrawLine(Pens.Blue, 10, 10, 20, 20); bitmap.Save("img.png", System.Drawing.Imaging.ImageFormat.Png); g.Dispose();
图像
Image 基类
System.Drawing
主要成员
属性
Height 图像高度(像素) HorizontalResolution 图像的水平分辨率(像素/英寸) Palette 获取或者设置图像的调色板(ColorPalette 对象) PhysicalDimension 获取描述图像维数的物理大小(单位:位图为像素,图元为0.01mm) PixelFormat 像素格式 RawFormat 获取图像格式(ImageFormat 对象) Size 返回描述图像的大小(像素) VerticalResolution 图像的垂直分辨率(像素/英寸) Width 图像宽度(像素)
方法
Dispose 释放此 Image 使用的所有资源 FromFile 根据文件中的数据创建图像 FromHbitmap 根据WindowsHBITMAP创建图像(GDI位图的句柄) FromStream 根据一个流创建图像 GetPixelFormatSize 获取指定像素格式的颜色深度(每个像素的位数,如24位、32位) GetBounds 返回图像的边界 GetThumbnaillmage 返回极小化的图像(即缩略图) RotateFlip 旋转、翻转图像 Save 将图像存入文件(可指定 ImageFormat 参数以保存不同格式的图片)
派生类
Bitmap
System.Drawing
GDI+ 位图
Metafile
System.Drawing.Imaging
图形图元文件
PixelFormat 枚举
System.Drawing.Imaging
指定图像中每个像素的颜色数据的格式
主要成员
例: Format24bppRgb 指定格式为每像素 24 位;红色、绿色和蓝色分量各使用 8 位 Format32bppArgb 指定格式为每像素 32 位;alpha、红色、绿色和蓝色分量各使用 8 位
ImageFormat 类
System.Drawing.Imaging
图像的文件格式
主要成员
静态属性
Bmp 位图 (BMP) 图像格式 Emf 增强型图元文件 (WMF) 图像格式 Exif 可交换图像文件 (Exif) 格式 Gif 图形交换格式 (GIF) 图像格式 Guid 表示此 Guid 对象的 ImageFormat 结构 Icon Windows 图标图像格式 Jpeg 联合图像专家组 (JPEG) 图像格式 MemoryBmp 内存中的位图的格式 Png W3C 可移植网络图形 (PNG) 图像格式 Tiff 标记图像文件格式 (TIFF) 图像格式 Wmf Windows 图元文件 (WMF) 图像格式
Bitmap 类
主要成员
方法
Clone 创建一个Bitmap的准确拷贝 FromHicon 从WindowsHICON的副本中创建Bitmap FromResource 从一个资源中创建Bitmap GetHbitmap 返回描述该对象的WindowsHBITMAP GetHicon 返回描述该对象的Windows HICON(可用于设置窗体的 Icon) GetPixel,SetPixel 获取或设置Bitmap中的像素(获取指定 x、y 坐标的颜色,坐标从0开始) MakeTransparent 使Bitmap的颜色透明 SetResolution 设置Bitmap的分辨率 LockBits 将 Bitmap 锁定到系统内存中,以便以编程方式访问(返回 BitmapData 对象) UnlockBits 从系统内存解锁 Bitmap
构造方法
Bitmap(Image) 从指定的现有图像初始化 Bitmap 类的新实例 Bitmap(Stream) 从指定的数据流初始化 Bitmap 类的新实例 Bitmap(String) 从指定的文件初始化 Bitmap 类的新实例 Bitmap(Image, Size) 从指定的现有图像(缩放到指定大小)初始化 Bitmap 类的新实例 Bitmap(Int32, Int32) 用指定的大小初始化 Bitmap 类的新实例 Bitmap(Stream, Boolean) 从指定的数据流初始化 Bitmap 类的新实例 Bitmap(String, Boolean) 从指定的文件初始化 Bitmap 类的新实例 Bitmap(Type, String) 从指定的资源初始化 Bitmap 类的新实例 Bitmap(Image, Int32, Int32) 从指定的现有图像(缩放到指定大小)初始化 Bitmap 类的新实例 Bitmap(Int32, Int32, Graphics) 用指定的大小和指定的 Graphics 对象的分辨率初始化 Bitmap 类的新实例 Bitmap(Int32, Int32, PixelFormat) 用指定的大小和像素格式初始化 Bitmap 类的新实例 Bitmap(Int32, Int32, Int32, PixelFormat, IntPtr) 用指定的大小、像素格式和像素数据初始化 Bitmap 类的新实例
Icon 类
System.Drawing
表示 Windows 图标
主要成员
方法
ExtractAssociatedIcon 返回指定文件中包含的图像的图标表示形式(例如 exe 的图标) Save 保存到指定流 ToBitmap 转换为位图
构造方法
Icon(Stream) 从指定的数据流初始化 Icon 类的新实例 Icon(String) 从指定的文件名初始化 Icon 类的新实例 Icon(Icon, Size) 初始化 Icon 类的新实例,并尝试查找与所请求的大小匹配的图标版本 Icon(Stream, Size) 从指定的流初始化 Icon 类的指定大小的新实例 Icon(String, Size) 从指定的文件初始化 Icon 类的指定大小的新实例 Icon(Type, String) 从指定程序集中的资源初始化 Icon 类的新实例 Icon(Icon, Int32, Int32) 初始化 Icon 类的新实例,并尝试查找与所请求的大小匹配的图标版本 Icon(Stream, Int32, Int32) 从指定的数据流用指定的宽度和高度初始化 Icon 类的新实例 Icon(String, Int32, Int32) 使用指定文件中的指定宽度和高度初始化 Icon 类的一个新实例
例:
设置窗体图标
form.Icon = SystemIcons.Question;
SystemIcons 类
System.Drawing
表示 Windows 系统中的图标
主要静态属性
Application 它包含默认的应用程序图标 (WIN32: IDI_APPLICATION) Asterisk 它包含系统星号图标 (WIN32: IDI_ASTERISK) Error 它包含系统错误图标 (WIN32: IDI_ERROR) Exclamation 它包含系统惊叹号图标 (WIN32: IDI_EXCLAMATION) Hand 它包含系统手状图标 (WIN32: IDI_HAND) Information 它包含系统信息图标 (WIN32: IDI_INFORMATION) Question 它包含系统问号图标 (WIN32: IDI_QUESTION) Shield 获取包含盾牌图标的 Icon 对象 Warning 它包含系统警告图标 (WIN32: IDI_WARNING) WinLogo 它包含 Windows 徽标图标 (WIN32: IDI_WINLOGO)
图像处理
BitmapData 类
System.Drawing.Imaging
位图的数据
主要属性
Reserved 保留。 请勿使用。 PixelFormat 获取或设置返回此 Bitmap 对象的 BitmapData 对象中像素信息的格式。 Width 获取或设置 Bitmap 对象的像素宽度。 这也可以看作是一个扫描行中的像素数。 Height 获取或设置 Bitmap 对象的像素高度。 有时也称作扫描行数。 Scan0 获取或设置位图中第一个像素数据的地址。 它也可以看成是位图中的第一个扫描行。 Stride 获取或设置 Bitmap 对象的跨距宽度(也称为扫描宽度)。跨距宽度是单行像素的宽度 ,会向上取整为4的整数倍(即“图像宽度 * 像素大小”不是 4 的倍数时,就会填几个空字节,填充的字节数可通过“bmpData.Stride - bmp.Width * 像素大小”获取)。 如果该数是正的,则位图是自上而下(则坐标原点为左上角?)。 如果该数为负数,则位图为自下而上(则坐标原点为左下角?)。 对于 24 位颜色深度的图像,每3个字节表示一个像素。对于 32 位颜色深度的图像,每4个字节表示一个像素。
像素点
颜色地址
(x,y) 像素点的颜色地址为:基地址 + x * 像素大小 + y * stride
颜色存储
根据 0xRRGGBB 可知,第1个字节为blue,第2个为green,第3个为red
根据 0xAARRGGBB 可知,第1字节为blue,第2为green,第3为red,第4为alpha
实例获取
Bitmap 实例的 LockBits 方法
例:
Bitmap bmp = new Bitmap(img1Path); BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, bmp.PixelFormat);
处理像素点基础例:
//例1:不使用指针的方法: using (Bitmap bmp = new Bitmap(img1Path)) { BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, bmp.PixelFormat); //锁定位图,获取位图数据对象 IntPtr ptr = bmpData.Scan0; //首个像素的指针,基地址 int count = Math.Abs(bmpData.Stride) * bmpData.Height; //图像数据总字节数 byte[] cache = new byte[count]; //数据缓存,方便操作 System.Runtime.InteropServices.Marshal.Copy(ptr, cache, 0, count); //复制出 //处理 int x = 0; int y = 0; int pixelSize = Image.GetPixelFormatSize(bmp.PixelFormat) / 8; //像素大小(字节数) //int nOffset = Math.Abs(bmpData.Stride) - bmp.Width * pixelSize; //像素行末尾填充的字节数 int pixelIndex = x * pixelSize + y * Math.Abs(bmpData.Stride); //像素在数据中的位置索引 const int BluePos = 0, GreenPos = 1, RedPos = 2, AlphaPos = 3; //颜色分量顺序位置 //设置像素颜色 cache[pixelIndex + BluePos] = byte.MaxValue; //Blue cache[pixelIndex + GreenPos] = byte.MaxValue; //Green cache[pixelIndex + RedPos] = byte.MaxValue; //Red System.Runtime.InteropServices.Marshal.Copy(cache, 0, ptr, count); //复制回 bmp.UnlockBits(bmpData); //解锁 ImageFormat format = ImageFormat.Png; bmp.Save($"处理后.{format.ToString().ToLower()}", format); }
颜色反转
每个颜色分量字节的值,替换为“255 - 原值”
using (Bitmap bmp = new Bitmap(img1Path)) { BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, bmp.PixelFormat); IntPtr ptr = bmpData.Scan0; int count = Math.Abs(bmpData.Stride) * bmpData.Height; byte[] cache = new byte[count]; Marshal.Copy(ptr, cache, 0, count); int pixelSize = Image.GetPixelFormatSize(bmp.PixelFormat) / 8; const int BluePos = 0, GreenPos = 1, RedPos = 2; for (int y = 0; y < bmp.Height; y++) { for (int x = 0; x < bmp.Width; x++) { int pixelIndex = x * pixelSize + y * Math.Abs(bmpData.Stride); //颜色反转 cache[pixelIndex + BluePos] = (byte)(byte.MaxValue - cache[pixelIndex + BluePos]); cache[pixelIndex + GreenPos] = (byte)(byte.MaxValue - cache[pixelIndex + GreenPos]); cache[pixelIndex + RedPos] = (byte)(byte.MaxValue - cache[pixelIndex + RedPos]); } } Marshal.Copy(cache, 0, ptr, count); bmp.UnlockBits(bmpData); }
变灰度图(含二值化)
每个颜色分量字节的值,替换为“0.114*blue + 0.587*green + 0.299*red”
using (Bitmap bmp = new Bitmap(img1Path)) { BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, bmp.PixelFormat); IntPtr ptr = bmpData.Scan0; int count = Math.Abs(bmpData.Stride) * bmpData.Height; byte[] cache = new byte[count]; Marshal.Copy(ptr, cache, 0, count); int pixelSize = Image.GetPixelFormatSize(bmp.PixelFormat) / 8; const int BluePos = 0, GreenPos = 1, RedPos = 2; for (int y = 0; y < bmp.Height; y++) { for (int x = 0; x < bmp.Width; x++) { int pixelIndex = x * pixelSize + y * Math.Abs(bmpData.Stride); //变灰度图 byte blue = cache[pixelIndex + BluePos]; byte green = cache[pixelIndex + GreenPos]; byte red = cache[pixelIndex + RedPos]; cache[pixelIndex + BluePos] = cache[pixelIndex + GreenPos] = cache[pixelIndex + RedPos] = (byte)(0.114 * blue + 0.587 * green + 0.299 * red); //灰度值 //二值化 //byte threshold = 128; //二值化阈值 //cache[pixelIndex + BluePos] = cache[pixelIndex + GreenPos] = cache[pixelIndex + RedPos] = (0.114 * blue + 0.587 * green + 0.299 * red) < threshold ? byte.MinValue : byte.MaxValue; //基于灰度值二值化 } } Marshal.Copy(cache, 0, ptr, count); bmp.UnlockBits(bmpData); }
加亮
每个颜色分量字节的值,加上一个常数
using (Bitmap bmp = new Bitmap(img1Path)) { BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, bmp.PixelFormat); IntPtr ptr = bmpData.Scan0; int count = Math.Abs(bmpData.Stride) * bmpData.Height; byte[] cache = new byte[count]; Marshal.Copy(ptr, cache, 0, count); int pixelSize = Image.GetPixelFormatSize(bmp.PixelFormat) / 8; const int BluePos = 0, GreenPos = 1, RedPos = 2; byte brightness = 50; //要加亮的值 for (int y = 0; y < bmp.Height; y++) { for (int x = 0; x < bmp.Width; x++) { int pixelIndex = x * pixelSize + y * Math.Abs(bmpData.Stride); //加亮 int blue = cache[pixelIndex + BluePos] + brightness; int green = cache[pixelIndex + GreenPos] + brightness; int red = cache[pixelIndex + RedPos] + brightness; if (blue < byte.MinValue) blue = byte.MinValue; if (blue > byte.MaxValue) blue = byte.MaxValue; if (green < byte.MinValue) green = byte.MinValue; if (green > byte.MaxValue) green = byte.MaxValue; if (red < byte.MinValue) red = byte.MinValue; if (red > byte.MaxValue) red = byte.MaxValue; cache[pixelIndex + BluePos] = (byte)blue; cache[pixelIndex + GreenPos] = (byte)green; cache[pixelIndex + RedPos] = (byte)red; } } Marshal.Copy(cache, 0, ptr, count); bmp.UnlockBits(bmpData); }
变红色调
红色分量变灰度值,蓝、绿色分量变为0
using (Bitmap bmp = new Bitmap(img1Path)) { BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, bmp.PixelFormat); IntPtr ptr = bmpData.Scan0; int count = Math.Abs(bmpData.Stride) * bmpData.Height; byte[] cache = new byte[count]; Marshal.Copy(ptr, cache, 0, count); int pixelSize = Image.GetPixelFormatSize(bmp.PixelFormat) / 8; const int BluePos = 0, GreenPos = 1, RedPos = 2; for (int y = 0; y < bmp.Height; y++) { for (int x = 0; x < bmp.Width; x++) { int pixelIndex = x * pixelSize + y * Math.Abs(bmpData.Stride); //变红色调 int blue = cache[pixelIndex + BluePos]; int green = cache[pixelIndex + GreenPos]; int red = cache[pixelIndex + RedPos]; cache[pixelIndex + BluePos] = 0; cache[pixelIndex + GreenPos] = 0; cache[pixelIndex + RedPos] = (byte)(0.114 * blue + 0.587 * green + 0.299 * red); } } Marshal.Copy(cache, 0, ptr, count); bmp.UnlockBits(bmpData); }
模糊
每个点的颜色变成周围颜色的平均值
using (Bitmap bmp = new Bitmap(img1Path)) { BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, bmp.PixelFormat); IntPtr ptr = bmpData.Scan0; int count = Math.Abs(bmpData.Stride) * bmpData.Height; byte[] cache = new byte[count]; Marshal.Copy(ptr, cache, 0, count); //复制出,计算用 int pixelSize = Image.GetPixelFormatSize(bmp.PixelFormat) / 8; int blurRadius = 3; //模糊半径 for (int y = 0; y < bmp.Height; y++) //遍历,从模糊区域中心开始,到模糊区域中心结束 { for (int x = 0; x < bmp.Width; x++) { int pixelIndex = x * pixelSize + y * Math.Abs(bmpData.Stride); //模糊 for (int c = 0; c < pixelSize; c++) //遍历像素的颜色分量 { int blockCompSum = 0; //模糊区域颜色分量和 int blockPixels = 0; //单块模糊区域像素数 int blurYStart = (y + 1) > blurRadius ? -blurRadius : -y; int blurYEnd = (bmp.Height - (y + 1)) < blurRadius ? bmp.Height - (y + 1) : blurRadius; int blurXStart = (x + 1) > blurRadius ? -blurRadius : -x; int blurXEnd = (bmp.Width - (x + 1)) < blurRadius ? bmp.Width - (x + 1) : blurRadius; for (int i = blurYStart; i <= blurYEnd; i++) //遍历单次模糊区域 { for (int j = blurXStart; j <= blurXEnd; j++) { blockCompSum += cache[(x + j) * pixelSize + (y + i) * Math.Abs(bmpData.Stride) + c]; blockPixels++; } } unsafe { byte* p = (byte*)ptr; //像素指针 p[pixelIndex + c] = (byte)(blockCompSum / blockPixels); //写回周围颜色分量的平均值 } } } } bmp.UnlockBits(bmpData); }
马赛克
每个小块内各点的颜色变成小块颜色的平均值
using (Bitmap bmp = new Bitmap(img1Path)) { BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, bmp.PixelFormat); IntPtr ptr = bmpData.Scan0; int count = Math.Abs(bmpData.Stride) * bmpData.Height; byte[] cache = new byte[count]; Marshal.Copy(ptr, cache, 0, count); int pixelSize = Image.GetPixelFormatSize(bmp.PixelFormat) / 8; int blockSize = 100; //马赛克块大小(像素数) for (int y = 0; y < bmp.Height; y += blockSize) //块遍历 { for (int x = 0; x < bmp.Width; x += blockSize) { //马赛克 for (int c = 0; c < pixelSize; c++) //遍历像素的颜色分量 { int blockCompSum = 0; //块颜色分量和 int blockPixels = 0; //计算的块内颜色分量数量 for (int i = 0; i < blockSize && i + y < bmp.Height; i++) //遍历块 { for (int j = 0; j < blockSize && j + x < bmp.Width; j++) { blockCompSum += cache[(x + j) * pixelSize + (y + i) * Math.Abs(bmpData.Stride) + c]; blockPixels++; } } unsafe { byte average = (byte)(blockCompSum / blockPixels); //块颜色分量的平均值 byte* p = (byte*)ptr; //像素指针 for (int i = 0; i < blockSize && i + y < bmp.Height; i++) //给块内各像素的颜色分量写平均值 { for (int j = 0; j < blockSize && j + x < bmp.Width; j++) p[(x + j) * pixelSize + (y + i) * Math.Abs(bmpData.Stride) + c] = average; } } } } } bmp.UnlockBits(bmpData); }
边缘增强
每个点变化量正比于该点与左上角颜色值的差值
using (Bitmap bmp = new Bitmap(img1Path)) { BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, bmp.PixelFormat); IntPtr ptr = bmpData.Scan0; int count = Math.Abs(bmpData.Stride) * bmpData.Height; byte[] cache = new byte[count]; Marshal.Copy(ptr, cache, 0, count); //复制出,计算用 int pixelSize = Image.GetPixelFormatSize(bmp.PixelFormat) / 8; for (int y = 1; y < bmp.Height; y++) //遍历,从第2个像素开始 { for (int x = 1; x < bmp.Width; x++) { int pixelIndex = x * pixelSize + y * Math.Abs(bmpData.Stride); //边缘增强 for (int c = 0; c < pixelSize; c++) //遍历像素的颜色分量 { int delta = cache[pixelIndex + c] - cache[(x - 1) * pixelSize + (y - 1) * Math.Abs(bmpData.Stride) + c]; //差值 int result = cache[pixelIndex + c] + delta; //正比 if (result < byte.MinValue) result = byte.MinValue; if (result > byte.MaxValue) result = byte.MaxValue; unsafe { byte* p = (byte*)ptr; //像素指针 p[pixelIndex + c] = (byte)result; //写回 } } } } bmp.UnlockBits(bmpData); }
裁剪下指定区域
调用 Bitmap 实例的 Clone 方法
public Bitmap CloneBitmap(Bitmap source, Rectangle rect) { var newImg = source.Clone(rect, source.PixelFormat); return newImg; }
WinForm
界面类继承链
Object <-- MarshalByRefObject <-- Component <-- Control <-- ScrollableControl <-- ContainerControl <-- UsreControl、Form
通用对话框
类
OpenFileDialog
命名空间: System.Windows.Forms Microsoft.Win32.FileDialog //WPF
打开文件
属性
Filter
格式
[描述(格式)]|[模式]
例
Image Files(*.bmp;*.jpg;*.gif)|*.bmp;*.jpg;*.gif|All files (*.*)|*.*
; 分隔格式 | 分隔模式、筛选器
SaveFileDialog
命名空间: System.Windows.Forms Microsoft.Win32.FileDialog
保存文件
FolderBrowserDialog
命名空间: System.Windows.Forms
选择文件夹
扩展
类似打开文件样式的打开文件夹对话框
OpenFolderDialog
using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Windows.Interop; /// <summary> /// Folder selector, vista-style. vista 以上系统支持。 /// </summary> /// <remarks> /// Source: /// https://www.magnumdb.com/search?q=IShellItem /// https://docs.microsoft.com/en-us/windows/win32/api/shobjidl_core/nn-shobjidl_core-ifiledialog /// https://docs.microsoft.com/en-us/windows/win32/api/shobjidl_core/ne-shobjidl_core-_fileopendialogoptions /// https://docs.microsoft.com/en-us/windows/win32/api/shobjidl_core/ne-shobjidl_core-sigdn /// </remarks> public class OpenFolderDialog { #region Native #region Constants /// <summary> /// Present an Open dialog that offers a choice of folders rather than files. /// </summary> public const uint DialogPickFolders = 0x00000020; /// <summary> /// Ensures that returned items are file system items (SFGAO_FILESYSTEM). /// Note that this does not apply to items returned by IFileDialog::GetCurrentSelection. /// </summary> public const uint DialogForceFileSystem = 0x00000040; /// <summary> /// Do not check for situations that would prevent an application from opening the selected file, such as sharing violations or access denied errors. /// </summary> public const uint DialogNoValidade = 0x00000100; /// <summary> /// Do not test whether creation of the item as specified in the Save dialog will be successful. /// If this flag is not set, the calling application must handle errors, such as denial of access, discovered when the item is created. /// </summary> public const uint DialogNoTestFileCreate = 0x00010000; /// <summary> /// Do not add the item being opened or saved to the recent documents list (SHAddToRecentDocs). /// </summary> public const uint DialogDontAddToRecent = 0x02000000; /// <summary> /// Ok return status. /// </summary> public const uint StatusOk = 0x0000; /// <summary> /// Returns the item's file system path, if it has one. Only items that report SFGAO_FILESYSTEM have a file system path. /// When an item does not have a file system path, a call to IShellItem::GetDisplayName on that item will fail. /// In UI this name is suitable for display to the user in some cases, but note that it might not be specified for all items. /// </summary> public const uint DisplayFileSysPath = 0x80058000; #endregion #region COM Imports [ComImport, ClassInterface(ClassInterfaceType.None), TypeLibType(TypeLibTypeFlags.FCanCreate), Guid("DC1C5A9C-E88A-4DDE-A5A1-60F82A20AEF7")] internal class FileOpenDialogRCW { } [ComImport, Guid("42F85136-DB7E-439C-85F1-E4075D135FC8"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal interface IFileDialog { [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), PreserveSig] uint Show([In, Optional] IntPtr hwndOwner); //IModalWindow [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] uint SetFileTypes([In] uint cFileTypes, [In, MarshalAs(UnmanagedType.LPArray)] IntPtr rgFilterSpec); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] uint SetFileTypeIndex([In] uint iFileType); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] uint GetFileTypeIndex(out uint piFileType); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] uint Advise([In, MarshalAs(UnmanagedType.Interface)] IntPtr pfde, out uint pdwCookie); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] uint Unadvise([In] uint dwCookie); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] uint SetOptions([In] uint fos); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] uint GetOptions(out uint fos); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] void SetDefaultFolder([In, MarshalAs(UnmanagedType.Interface)] IShellItem psi); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] uint SetFolder([In, MarshalAs(UnmanagedType.Interface)] IShellItem psi); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] uint GetFolder([MarshalAs(UnmanagedType.Interface)] out IShellItem ppsi); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] uint GetCurrentSelection([MarshalAs(UnmanagedType.Interface)] out IShellItem ppsi); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] uint SetFileName([In, MarshalAs(UnmanagedType.LPWStr)] string pszName); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] uint GetFileName([MarshalAs(UnmanagedType.LPWStr)] out string pszName); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] uint SetTitle([In, MarshalAs(UnmanagedType.LPWStr)] string pszTitle); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] uint SetOkButtonLabel([In, MarshalAs(UnmanagedType.LPWStr)] string pszText); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] uint SetFileNameLabel([In, MarshalAs(UnmanagedType.LPWStr)] string pszLabel); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] uint GetResult([MarshalAs(UnmanagedType.Interface)] out IShellItem ppsi); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] uint AddPlace([In, MarshalAs(UnmanagedType.Interface)] IShellItem psi, uint fdap); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] uint SetDefaultExtension([In, MarshalAs(UnmanagedType.LPWStr)] string pszDefaultExtension); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] uint Close([MarshalAs(UnmanagedType.Error)] uint hr); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] uint SetClientGuid([In] ref Guid guid); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] uint ClearClientData(); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] uint SetFilter([MarshalAs(UnmanagedType.Interface)] IntPtr pFilter); } [ComImport, Guid("43826D1E-E718-42EE-BC55-A1E261C37BFE"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal interface IShellItem { [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] uint BindToHandler([In] IntPtr pbc, [In] ref Guid rbhid, [In] ref Guid riid, [Out, MarshalAs(UnmanagedType.Interface)] out IntPtr ppvOut); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] uint GetParent([MarshalAs(UnmanagedType.Interface)] out IShellItem ppsi); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] uint GetDisplayName([In] uint sigdnName, out IntPtr ppszName); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] uint GetAttributes([In] uint sfgaoMask, out uint psfgaoAttribs); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] uint Compare([In, MarshalAs(UnmanagedType.Interface)] IShellItem psi, [In] uint hint, out int piOrder); } #endregion [DllImport("shell32.dll", CharSet = CharSet.Unicode, SetLastError = true)] internal static extern int SHCreateItemFromParsingName([MarshalAs(UnmanagedType.LPWStr)] string pszPath, IntPtr pbc, ref Guid riid, [MarshalAs(UnmanagedType.Interface)] out IShellItem ppv); #endregion #region Properties /// <summary> /// Gets or sets the descriptive text displayed above the tree view control in the dialog box. /// <para>获取或设置对话框中树状视图控件上方显示的描述性文本。</para> /// </summary> public string Description { get; set; } /// <summary> /// Gets or sets the selected folder path. /// If some path is set before opening the dialog, that path is used as a folder that is always /// selected when the dialog is opened, regardless of previous user action. /// <para>获取或设置所选文件夹路径。</para> /// </summary> public string SelectedPath { get; set; } /// <summary> /// Default folder to be used if no recent folder available. /// <para>如果没有可用的最新文件夹,则使用默认文件夹。</para> /// </summary> public string DefaultFolder { get; set; } #endregion public bool ShowDialog(IWin32Window owner = null) { //ReSharper disable once SuspiciousTypeConversion.Global var frm = (IFileDialog)new FileOpenDialogRCW(); //Set folder picker options. uint options; frm.GetOptions(out options); options |= DialogPickFolders | DialogForceFileSystem | DialogNoValidade | DialogNoTestFileCreate | DialogDontAddToRecent; frm.SetOptions(options); if (!string.IsNullOrWhiteSpace(Description)) frm.SetTitle(Description); if (SelectedPath != null) { //IShellItem var riid = new Guid("43826D1E-E718-42EE-BC55-A1E261C37BFE"); IShellItem directoryShellItem; if (SHCreateItemFromParsingName(SelectedPath, IntPtr.Zero, ref riid, out directoryShellItem) == StatusOk) frm.SetFolder(directoryShellItem); } if (DefaultFolder != null) { //IShellItem var riid = new Guid("43826D1E-E718-42EE-BC55-A1E261C37BFE"); IShellItem directoryShellItem; if (SHCreateItemFromParsingName(DefaultFolder, IntPtr.Zero, ref riid, out directoryShellItem) == StatusOk) frm.SetDefaultFolder(directoryShellItem); } IShellItem shellItem; IntPtr pszString; if (frm.Show(owner != null ? owner.Handle : IntPtr.Zero) != StatusOk || frm.GetResult(out shellItem) != StatusOk || shellItem.GetDisplayName(DisplayFileSysPath, out pszString) != StatusOk || pszString == IntPtr.Zero) return false; try { SelectedPath = Marshal.PtrToStringAuto(pszString); return true; } finally { Marshal.FreeCoTaskMem(pszString); } } }
FontDialog
命名空间: System.Windows.Forms
字体选择
ColorDialog
命名空间: System.Windows.Forms
选择颜色
PrintDialog
命名空间: System.Windows.Forms
打印
PrintPreviewDialog
命名空间: System.Windows.Forms
打印预览
PageSetupDialog
命名空间: System.Windows.Forms
页面设置
MDI
多文档界面应用程序
实现步骤
1 设置主窗体的 IsMdiContainer 属性为 true
2 将子窗体的 MdiParent 属性设置为 主窗体
Windows
服务
项目
新建
Windows 服务(.NET Framework)
主要结构
Program.cs
入口
Service1.cs
实现
主要步骤
(1)服务实现
在 Service1.cs 代码中
重写
void OnStart(string[] args)
服务启动时。
void OnStop()
服务停止时。
(2)安装程序
在 Service1.cs 设计器中
空白处右键“添加安装程序”
在添加的 ProjectInstaller.cs 设计器中
设置 serviceProcessInstaller1 对象
安装一个可执行文件,该文件包含扩展 ServiceBase 的类。 该类由安装实用工具(如 InstallUtil.exe)在安装服务应用程序时调用。
的 Account 属性为 LocalSystem
设置 Service1 的 serviceInstaller1 对象
安装一个类,该类扩展 ServiceBase 来实现服务。 在安装服务应用程序时由安装实用工具调用该类。
的 ServiceName、DisplayName、Description 属性
(3)服务管理
新建控制台项目
添加安装、卸载等管理代码
using System; using System.Collections; using System.Configuration.Install; using System.ServiceProcess; namespace WindowsServiceManager1 { class Program { static void Main(string[] args) { string serviceFilePath = @"D:\Services\Service1.exe"; string serviceName = "Service1"; if (ExistsService(serviceName)) { StopService(serviceName); UninstallService(serviceFilePath); } InstallService(serviceFilePath); StartService(serviceName); } /// <summary> /// 安装服务。 /// </summary> /// <param name="serviceFilePath">服务程序集路径</param> private static void InstallService(string serviceFilePath) { using (AssemblyInstaller installer = new AssemblyInstaller()) { installer.UseNewContext = true; installer.Path = serviceFilePath; IDictionary savedState = new Hashtable(); installer.Install(savedState); installer.Commit(savedState); } } /// <summary> /// 启动服务。 /// </summary> /// <param name="serviceName">系统服务名</param> private static void StartService(string serviceName) { using (ServiceController controller = new ServiceController(serviceName)) { if (ServiceControllerStatus.Stopped == controller.Status) { controller.Start(); } } } /// <summary> /// 停止服务。 /// </summary> /// <param name="serviceName">系统服务名</param> private static void StopService(string serviceName) { using (ServiceController controller = new ServiceController(serviceName)) { if (ServiceControllerStatus.Running == controller.Status) { controller.Stop(); } } } /// <summary> /// 卸载服务。 /// </summary> /// <param name="serviceFileName">服务程序集路径</param> private static void UninstallService(string serviceFileName) { using (AssemblyInstaller installer = new AssemblyInstaller()) { installer.UseNewContext = true; installer.Path = serviceFileName; IDictionary savedState = null; installer.Uninstall(savedState); } } /// <summary> /// 服务是否存在。 /// </summary> /// <param name="serviceName"></param> /// <returns></returns> public static bool ExistsService(string serviceName) { ServiceController[] services = ServiceController.GetServices(); foreach (ServiceController service in services) { if (string.Equals(service.ServiceName, serviceName, StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } } }
添加应用程序清单 app.manifest 文件,并设置管理员
创建快捷方式
步骤
(1)程序集添加“Windows Script Host Object Model”COM 引用
(2)创建 WshShell 类型实例,再创建 IWshShortcut 实例创建
例:
/// <summary> /// 创建快捷方式 /// </summary> /// <param name="lnkDir">快捷方式目录</param> /// <param name="lnkNameNoExt">快捷方式文件名(无后缀部分)</param> /// <param name="exePath">exe 文件路径(文件需存在)</param> /// <param name="startupDir">起始位置</param> /// <param name="iconPath">图标路径</param> /// <param name="args">运行参数</param> /// <param name="remark">备注</param> /// <returns></returns> public static bool CreateShortcut(string lnkDir, string lnkNameNoExt, string exePath, string startupDir = null, string iconPath = ",0", string args = "", string remark = "") { try { string lnkExt = ".lnk"; string linkPath = System.IO.Path.Combine(lnkDir, lnkNameNoExt + lnkExt); //快捷方式文件路径 if (!System.IO.File.Exists(exePath)) return false; if (null == startupDir) startupDir = System.IO.Path.GetDirectoryName(exePath); if (!System.IO.Directory.Exists(lnkDir)) System.IO.Directory.CreateDirectory(lnkDir); IWshRuntimeLibrary.WshShell shell = new IWshRuntimeLibrary.WshShell(); IWshRuntimeLibrary.IWshShortcut shortcut = (IWshRuntimeLibrary.IWshShortcut)shell.CreateShortcut(linkPath); shortcut.TargetPath = exePath; //目标 shortcut.WorkingDirectory = AddDirectorySeparatorChar(startupDir); //起始位置 shortcut.WindowStyle = 1; //运行方式:窗口:1常规、3最大化、7最小化 shortcut.Description = remark; //备注 shortcut.IconLocation = iconPath; //图标路径 shortcut.Arguments = args; //运行参数 shortcut.Save(); //保存创建 return true; } catch (Exception ex) { return false; } } public static string AddDirectorySeparatorChar(string directory) { return string.IsNullOrWhiteSpace(directory) ? directory : directory.EndsWith(System.IO.Path.DirectorySeparatorChar.ToString()) || directory.EndsWith(System.IO.Path.AltDirectorySeparatorChar.ToString()) || directory.EndsWith(System.IO.Path.VolumeSeparatorChar.ToString()) ? directory : directory + System.IO.Path.DirectorySeparatorChar; }
文件关联
刷新
例:
//导入: public class Shell32 { [DllImport("shell32.dll")] public static extern void SHChangeNotify(HChangeNotifyEventID wEventId, HChangeNotifyFlags uFlags, IntPtr dwItem1, IntPtr dwItem2); } [Flags] public enum HChangeNotifyEventID { SHCNE_ASSOCCHANGED = 0x08000000, } [Flags] public enum HChangeNotifyFlags : UInt32 { SHCNF_IDLIST = 0x0000, } //调用 Shell32.SHChangeNotify(HChangeNotifyEventID.SHCNE_ASSOCCHANGED, HChangeNotifyFlags.SHCNF_IDLIST, IntPtr.Zero, IntPtr.Zero);
管理员权限
检查当前权限
例:
var identity = System.Security.Principal.WindowsIdentity.GetCurrent(); //当前 Windows 用户 var principal = new System.Security.Principal.WindowsPrincipal(identity); //允许代码检查 Windows 用户的 Windows 组成员身份 bool isInRole = principal.IsInRole(System.Security.Principal.WindowsBuiltInRole.Administrator); //确定当前主体是否属于指定的 Windows 用户组
以管理员权限执行程序
当前程序直接提权
添加应用程序清单 app.manifest 文件,并设置管理员
其他程序间接提权
例:
var proc = new Process(); proc.StartInfo.FileName = "cmd.exe"; if (Environment.OSVersion.Version.Major > 5) //XP 系统(不含)以上时。可先检查当前权限再决定 proc.StartInfo.Verb = "runas"; //使用 runas 命令提权 proc.Start();
Windows 管理对象
参考
ManagementObjectSearcher 类
ManagementEventWatcher 类
例:
获取物理内存
/// <summary> /// 获取物理内存大小(MB) /// </summary> /// <returns></returns> public static double GetTotalPhysicalMemory() { double capacity = 0; try { //(new ManagementClass("Win32_PhysicalMemory")).GetInstances() 等效于 (new ManagementObjectSearcher("Select * From Win32_PhysicalMemory")).Get() foreach (ManagementObject mo in (new ManagementClass("Win32_PhysicalMemory")).GetInstances()) capacity += long.Parse(mo.Properties["Capacity"].Value.ToString()); capacity = capacity / 1024 / 1024; } catch (Exception ex) { capacity = 0d; } return capacity; }
防止系统休眠
例:
public class Kernel32 { /// <summary> /// 设置线程执行状态 /// </summary> /// <param name="esFlags"></param> /// <returns></returns> [DllImport("kernel32.dll")] public static extern UInt32 SetThreadExecutionState(ExecutionState esFlags); } [Flags] public enum ExecutionState : UInt32 { SYSTEM_REQUIRED = ((UInt32)0x00000001), //通过重置系统空闲计时器强制系统处于工作状态 DISPLAY_REQUIRED = ((UInt32)0x00000002), //通过重置显示空闲计时器强制显示打开 USER_PRESENT = ((UInt32)0x00000004), //不支持此值 AWAYMODE_REQUIRED = ((UInt32)0x00000040), //启用客场模式。 必须使用 ES_CONTINUOUS 指定此值 CONTINUOUS = ((UInt32)0x80000000), //通知系统,要设置的状态应保持有效状态,直到下一次调用使用 } public class SystemSleepManagement { /// <summary> ///阻止系统休眠,直到线程结束恢复休眠策略 /// </summary> /// <param name="includeDisplay">是否阻止关闭显示器</param> public static void PreventSleep(bool includeDisplay = true) { if (includeDisplay) Kernel32.SetThreadExecutionState(ExecutionState.SYSTEM_REQUIRED | ExecutionState.DISPLAY_REQUIRED | ExecutionState.CONTINUOUS); else Kernel32.SetThreadExecutionState(ExecutionState.SYSTEM_REQUIRED | ExecutionState.CONTINUOUS); } /// <summary> ///恢复系统休眠策略 /// </summary> public static void ResotreSleep() { Kernel32.SetThreadExecutionState(ExecutionState.CONTINUOUS); } /// <summary> ///重置系统休眠计时器 /// </summary> /// <param name="includeDisplay">是否阻止关闭显示器</param> public static void ResetSleepTimer(bool includeDisplay = true) { if (includeDisplay) Kernel32.SetThreadExecutionState(ExecutionState.SYSTEM_REQUIRED | ExecutionState.DISPLAY_REQUIRED); else Kernel32.SetThreadExecutionState(ExecutionState.SYSTEM_REQUIRED); } }
系统
剪贴板
命名空间:System.Windows
Clipboard 类
剪贴板
DataObject 类
提供 IDataObject 接口的基本实现,该接口为传输数据定义与格式无关的机制
跨平台调用
句柄
类型
IntPtr
HandleRef
SafeHandle
操作系统句柄的包装抽象类
属性
IsInvalid
句柄值是否无效
IsClosed
IsClosed
主要方法
DangerousGetHandle()
返回 handle 字段的值
SetHandle(IntPtr handle)
设置句柄为指定的值
Dispose()
释放 SafeHandle 类使用的资源
SafeFileHandle
File 句柄的包装类
PInvoke 参考
使用例:
dll 一般的搜索顺序
1 应用程序目录
2 System 系统目录
3 Windows 操作系统目录
4 当前目录
5 环境变量 PATH 目录
例:
C++DLL
头文件: #pragma once #ifndef DLLTEST_H #define DLLTEST_H #include "pch.h" #ifdef DLLTEST_EXPORTS #define DLLCALC_API __declspec(dllexport) #else #define DLLCALC_API __declspec(dllimport) #endif struct Book { unsigned char thickness; int price; unsigned char data[6]; }; EXTERN_C DLLCALC_API void __stdcall MemberAdd(Book* pBook, unsigned char add); EXTERN_C DLLCALC_API int __stdcall ArrayMemberAdd(Book inBooks[], Book inoutBooks[], int len1, int len2, unsigned char add); EXTERN_C DLLCALC_API int __stdcall GetArrayMember(Book* pOutBooks, int count, unsigned char step); #endif // !DLLTEST_H
C#DLL导入
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Runtime.InteropServices; namespace DllTestPInvoke { public struct Book { public byte thickness; public int price; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] public byte[] data; public void Init(byte init) { thickness = init; price = init; data = new byte[6]; for (int i = 0; i < data.Length; i++) { data[i] = init; } } } public class DllTest { [DllImport("DllTest.dll")] public static extern void MemberAdd(ref Book pBook, byte add); [DllImport("DllTest.dll")] public static extern int ArrayMemberAdd([In] Book[] inBooks, [In, Out] Book[] inoutBooks, int len1, int len2, byte add); [DllImport("DllTest.dll")] public static extern int GetArrayMember([Out] Book[] pOutBooks, int count, byte step); } }
Form
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using System.Drawing.Drawing2D; using Utility; using DllTestPInvoke; namespace WinFormTest { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.AllPaintingInWmPaint, true); //优化绘制操作 dataGridView1.DoubleBuffered(true); } private void button1_Click(object sender, EventArgs e) { #region PInvoke Book book1 = new Book(); book1.Init(1); DllTest.MemberAdd(ref book1, 1); Book[] books1 = new Book[5]; for (int i = 0; i < books1.Length; i++) { books1[i].Init(2); } Book[] books2 = new Book[6]; for (int i = 0; i < books2.Length; i++) { books2[i].Init(3); } int a = DllTest.ArrayMemberAdd(books1, books2, books1.Length, books2.Length, 2); Book[] books3 = new Book[7]; for (int i = 0; i < books3.Length; i++) { books3[i].Init(4); } int b = DllTest.GetArrayMember(books3, books3.Length, 3); #endregion } private void button2_Click(object sender, EventArgs e) { } private void dataGridView1_RowsAdded(object sender, DataGridViewRowsAddedEventArgs e) { dataGridView1.FirstDisplayedScrollingRowIndex = dataGridView1.Rows.Count - 1; //显示最后一行 } private void Form1_Paint(object sender, PaintEventArgs e) { Tool.FormGradient(this, e, Color.FromArgb(127, 101, 192), Color.FromArgb(48, 189, 146), LinearGradientMode.Vertical); } private void tabControl1_DrawItem(object sender, DrawItemEventArgs e) { //Tool.TabControlColor(sender as TabControl, e, Color.FromArgb(148, 188, 255), Color.FromArgb(123, 191, 234), Color.FromArgb(34, 143, 189), (sender as TabControl).Font, Color.FromArgb(241, 237, 189), Color.FromArgb(240, 117, 68)); } private void textBox1_DragEnter(object sender, DragEventArgs e) { //控件的AllowDrop属性需设置为true if (e.Data.GetDataPresent(DataFormats.FileDrop)) { e.Effect = DragDropEffects.All; } else { e.Effect = DragDropEffects.None; } } private void textBox1_DragDrop(object sender, DragEventArgs e) { string[] paths = (string[])e.Data.GetData(DataFormats.FileDrop); textBox1.Text = string.Join(Environment.NewLine, paths); } } }
收藏
Visual Studio
扩展
Viasfora
彩虹括号
Semantic Colorizer
代码颜色
Word Highlight With Margin
选中高亮
C# outline
折叠花括号
需要禁用内置大纲 工具-选项-文本编辑器-C#-高级-大纲,取消勾选Show outlining of code level constructs
Indent Guides
缩进垂直线
ResXManager
管理 resx 资源文件
快捷键
Ctrl+T
转到所有(如字段、方法、类、文件等)
NuGet
程序集引用出现三角警告?
重新安装包
Update-Package -reinstall
vs->工具->NuGet包管理器->程序包管理控制台