本文介绍c#的实用知识点
写在前面(通识)
vs常用快捷键
F5 调试运行程序 ctrl F5 不调试运行程序 F11 逐条语句调试 F10 逐过程调试程序 注释快捷键 ctrl + k + c 代码格式化 ctrl + A + k + F 强制智能提示 ctrl + J
面相对象语言三大特性
封装性,重复代码共用 继承性,类,接口等的继承 多态性,不同的子类调用父类的方法,执行效果不一样
c#中的访问修饰符
private 本类内部可以使用 protected 本类内部和子类内部可以使用 internal 当前程序集中可用 protected internal 当前程序集中,并且是的当前类和子类内部可用 public 访问无限制 类成员不写访问修饰符默认是private 类不写访问修饰符默认是internal 命名空间中定义的成员只能是public或者internal
属性和字段
private string _str = "aaa"; // 字段 internal int num { get; set; } // 属性
方法的重载
方法名相同,只要参数类型、个数或者顺序不同时就认为是不同的函数
泛型
泛型是C#中的一大特色,也是基础 泛型就是将类型参数化 泛型参数可以使用where做进一步限制 where T: someClass 限制T是继承自someClass类的 where T: new() 限制T是可以实例化的 where T: someInterface 限制T是继承自someInterface接口的 where T: U 限制T是继承自其他类型参数的 使用demo public class Demo1
: Test1 where T: Test2 { } public interface Test1 : Test2 { } public interface Test2 { } 泛型方法 public void Fn (T[] a) { } 测量代码的运行时间
Stopwatch watch = new Stopwatch(); watch.Start(); // ... watch.Stop(); TimeSpan time = watch.Elapsed;
生成guid
String str = Guid.NewGuid().ToString();
嵌套引号的写法
string str = @"aaaa""aa""";
扩展方法
指定任意一个顶级静态类就可以定义任意类型的扩展方法 定义扩展方法 public static class Demo { public static void Fn(this int a) { Console.WriteLine("aa"); } } 使用扩展方法 int num = 222; num.Fn();
c#基础
变量
声明变量
声明变量 string str1 = 'aa', str2 添加?号,表示可以赋值null,int? i = 1; 等价于 Nullable
a = null; 成员变量
static string str; string str1; public void Fn() { str = "aaa"; str1 = "bbb"; }
局部变量
在代码块中定义的变量,只在代码块中有效 嵌套的子作用域中不能有父作用域中的同名变量
常量
const string str2 = "aaa"; 在声明的时候必须赋值,并且后续无法修改
数据类型
值类型
整数类型 sbyte -128~127 byte 0~255 short -32768~327767 ushort 0~65535 int -2147483648~2147483647 uint 0~4294967295 long -9223372036854775808~-9223372036854775807 ulong 0~18446744073709551615 浮点类型 float 整数位和小数位加起来最大不超过7位数 double 整数位和小数位加起来最大不超过15位数 默认情况下都是double类型,要使用float类型必须强转 double num = 1.2; float num1 = 1.3f; float num2 = 1.4F; float num3 = (float)num; 布尔类型 bool a = true; 字符类型 表示单个字符 char c = 'a'; char.IsLetterOrDigit(c); // 判断字符是不是字母和数字 结构类型 声明结构 public struct Person { const int Age = 18; // 声明常量 public string Name; // 声明变量,不能指定初始值 public Person(string name) // 构造函数 { Name = name; } public string Fn() // 声明方法 { return Name; } } 使用结构 不使用new Person demo; demo.Name = "小叶"; 使用new Person demo = new Person("小叶"); Console.WriteLine(demo.Fn());
引用类型
class类 ** 构造函数和析构函数 构造函数使用new自动调用 静态构造函数 不能使用访问修饰符 不能传递参数 使用此类立即自动调用构造函数,并且只会调用一次 析构函数,当此类即将被程序销毁时调用 public class Demo { public Demo() // 构造函数 { Thread.Sleep(3000); Console.WriteLine("构造函数执行了"); } ~Demo() { Console.WriteLine("析构函数执行了"); } } ** 类的继承 基本写法 public class Demo1 { protected string _Name { get; set; } = "小叶"; public void Fn() { } } public class Demo2 : Demo1 { public void SayHello() { base.Fn(); // base访问父类中的成员 Console.WriteLine("你好" + _Name); } } 构造函数的继承 父类中有包含参数的构造函数子类无法继承,子类继承需要父类无参数构造函数 解决办法如下 重新实现一个无参数构造函数 public class Demo1 { public Demo1(string str) { } public Demo1() { } // public Demo1(): this("aaa") { } } public class Demo2 : Demo1 { } 使用base给父类构造函数传递参数 public class Demo1 { public Demo1(string str) { } } public class Demo2 : Demo1 { public Demo2(string str) : base(str) { } } ** 隐藏方法 当子类中有和父类中方法同名的方法时,最好使用new隐藏 public class Demo1 { public void Fn() { Console.WriteLine("aa"); } } public class Demo2 : Demo1 { public new void Fn() { Console.WriteLine("bb"); } } ** 虚方法 除了可以使用new隐藏外,还可以使用override重写virtual方法 public class Demo1 { public virtual void Fn() { Console.WriteLine("aa"); } } public class Demo2 : Demo1 { public override void Fn() { Console.WriteLine("bb"); } } ** 抽象类 抽象类无法实例化 抽象方法只能在子类中使用override实现 抽象方法不能使用virtual,static,private public abstract class Demo1 { public abstract void Fn(); } public class Demo2 : Demo1 { public override void Fn() { } } ** 密封类 密封类无法继承,不能使用abstract 密封类成员使用protected和virtual无意义 密封方法必须是override父类的方法 public sealed class Demo2: Demo1 { public sealed override void Fn() { base.Fn(); } } ** 分布类 当你想把一个类像命名空间一样分布在多个文件中,那么分布类是你唯一的选择 public partial class Demo { public int _num1 = 1; } public partial class Demo { public int _num2 = 2; } 简直就是黑科技 ** 使用操作符简写类实例化过程 public class Demo { int Num { get; set; } public static implicit operator Demo(int num) { return new Demo() { Num = num }; } } 使用 Demo text = 11; 即可初始化一个Demo实例 ** 类的浅拷贝 public class Demo { public int Age { get; set; } public int Name { get; set; } public Demo ShallowCopy() { return this.MemberwiseClone() as Demo; } } ** 类的深拷贝 [Serializable] public class Demo { public int Age { get; set; } public int Name { get; set; } public Demo DeepCopy () { BinaryFormatter bf = new BinaryFormatter(); using(MemoryStream ms = new MemoryStream()) { bf.Serialize(ms, this); ms.Position = 0; return bf.Deserialize(ms) as Demo; } } } 字符串类型 string str = null; // 空字符串 string str = string.Empty; // 0长度字符串 ** 比较两个字符串是否相等 str1 == str2 str1.CompareTo(str2) // 相等返回0,str1大于str2返回1,小于返回-1 string.Equals(str1, str2) // 相等返回true,不等返回false str1.Equals(str2) string.Compare(str1,str2[,true]) // 相等返回0,str1大于str2返回1,小于返回-1。第三个可选的布尔值,如果true表示忽略大小写 ** 字符串格式化 一般字符串格式化 string str = string.Format("{0},{1}!", "aa", "bb"); // aa,bb! 日期格式化 string str = string.Format("{0:D}", DateTime.Now); // 2017年12月31日 相关的格式化参数如下 d 表示YYYY-MM-dd D 表示YYYY年MM月dd日 t 表示hh:mm T 表示hh:mm:ss f 表示YYYY年MM月dd日 hh:mm F 表示YYYY年MM月dd日 hh:mm:ss g 表示YYYY-MM-dd hh:mm G 表示YYYY-MM-dd hh:mm:ss M或者m 表示 MM月dd日 Y或者y 表示YYYY年MM月 ** 字符串截取 string str = "C#好简单啊".Substring(0, 2); 从索引为0的位置开始截取2个字符 ** 分割字符串 string str = "今天是|元旦佳节,一个!人"; string[] arr = str.Split(new char[] { '|', ',', '!' }); // ["今天是", "元旦佳节", "一个", "人"] ** 插入字符串 string str = "aaaccc".Insert(3, "bbb"); // aaabbbcc ** 字符串填充 string str = "a".PadLeft(7,'a'); // aaaaaaa string str = "a".PadRight(7,'b'); // abbbbbb 第一个参数是填充后的总字符串长度 ** 删除字符串 string str = "abcdef".Remove(2); // ab 从索引位2开始删 string str = "abcdef".Remove(2, 2); // abef 从索引位2开始删,删除两个字符 ** 字符串复制 copy复制 string str = string.Copy("aaa"); CopyTo复制,将字符串的一部分复制到字符数组中 char[] result = new char[10]; "aaaa".CopyTo(1, result, 1, 2); 参数一,字符串的开始索引 参数二,字符数组 参数三,字符数组中的起始索引 参数四,字符串要复制的字符个数 ** 字符串替换 string str = "abs,dg".Replace(',', char.MinValue); ** 字符串去首位空格 str.Trim(); ** 字符串转成字符数组 string str = "你好吗?"; char[] arr = str.ToCharArray(); str = new string(arr); // 字节数组转字符串 ** 字符串转成字节数组 byte[] arr = Encoding.UTF8.GetBytes("agagteg"); ** 字节转成字符串 string str = Encoding.UTF8.GetString(new byte[] { 1, 2, 3, 4 }); ** 字符串常量池 由于字符串的不可变性,所有的字符串的引用都存储在池中 string str = String.Intern("aaa"); // 用于在池中查找字符串的引用,如果存在则直接返回引用,如果不存在则创建字符串并且返回引用 string str = String.IsInterned("aaa"); // 如果存在返回引用,如果不存在返回null stringBuilder类型 string对象创建后是不可变的,stringBuilder的作用是创建可变的字符串 StringBuilder str = new StringBuilder("aaabbbccc", 10); // 创建10个字符长度的字符串 str.Append("ddd"); // 追加 str.AppendFormat("{0}!!!!", "ddd"); // 格式化后追加 str.Insert(9, "ddd"); // 在指定索引位插入字符串 str.Remove(0, 3); // 指定开始索引位删除指定个数的字符 str.Replace("ccc", "!"); // 字符串替换 DateTime类型 DateTime time = DateTime.Now; // 获取系统的当前时间 DateTime time = DateTime.Today; // 获取当前日期 DateTime time = DateTime.Today.AddDays(1); // 获取明天的日期 DateTime time = new DateTime(2017, 10, 3); // 设置日期 DateTime time = DateTime.Parse("2017-10-13"); // 将字符串转成日期 int part = time.Year; // 获取年 int part = time.Month; // 获取月 int part = time.Day; // 获取天 int part = time.Hour; // 获取时 int part = time.Minute; // 获取分钟 int part = time.Second; // 获取秒 Random类型 Random random = new Random(); int number = random.Next(); // 生成任意随机数 int number = random.Next(100); // 生成小于100的随机数 int number = random.Next(0, 10); // 生成0~10之间的随机数 委托类型 委托就是用来表示匿名函数的类型 基本原理 static void Main(string[] args) { Dlg d = new Dlg(Fn); // 或者 Dlg d = Fn; d(); // 或者 d.Invoke(); Console.ReadKey(); } public delegate void Dlg(); // 声明委托 public static void Fn() { Console.WriteLine("调用了"); } 当做类型使用 public delegate void Dlg(); public static void Fn(Dlg fn) { fn(); } 当做匿名函数使用 Dlg fn = delegate () { }; Dlg fn = () => { }; public delegate void Dlg(); 泛型委托 Dlg
fn = (str) => { }; public delegate void Dlg (T str); 内置委托类型 Action类型,没有返回值 Action Fn = (str) => { }; Func类型,有返回值 Func Fn = (a, b) => a + b; 多播委托 Action fn = () => { Console.WriteLine(1); }; fn += () => { Console.WriteLine(2); }; fn += () => { Console.WriteLine(3); }; 事件委托 事件委托使用event关键字修饰,在外界赋值要使用+=或者-=,并且只能在类的内部调用 public static class Demo { public static event Action Fn; static void Fn1() { Fn(); } // 外界无法调用此委托 } Demo.Fn += () => { Console.WriteLine(3); }; // 外界赋值只能这样赋值 异步委托 Func fn = a => { Thread.Sleep(2000); return a; }; IAsyncResult ir = fn.BeginInvoke(2, a => { // 此处的回调函数,c#会单独开辟一个线程执行 Console.WriteLine(a.AsyncState); }, "我是回调函数的参数"); Console.WriteLine(fn.EndInvoke(ir)); // 此处阻塞当前主线程,等待执行完毕 if (ir.IsCompleted) { Console.WriteLine("执行完成"); } Console.WriteLine("主线程"); 上面代码的执行结果如下 2 执行完成 我是回调函数的参数 // 此结果执行时机不确定 主线程 枚举类型
枚举的作用就是使用属性代表数值,增加程序的可读性 枚举,默认从0开始一次递增,可以手动赋值 enum myEnum { first, seconde, third } (int)myEnum.first // 0
类型转换
隐式转换,低精度的值和隐式转换成高精度同类型的值,反过来不行 显示转换 将高精度值转成低精度 long j = 2; int i = (int)j; 或者 long j = 2; int i = Convert.ToInt32(j); 字符串转数字 int.Parse("111"); 类型强转 a as b // 容错处理 装箱和拆箱 将值类型转化成引用类型叫做装箱,反过来叫做拆箱 装箱 int i = 111; object obj = i; 拆箱 object i = 111; int obj = (int)i;
相等比较
ReferenceEquals 比较引用,但是在比较字符串的时候比较的是值 bool isEqual = object.ReferenceEquals(new { }, new { }); // false Equals 比较值 1.Equals(2); == 比较值 1 == 2
表达式和运算符
算数运算符 + - * / % 赋值运算符 = += -= /= *= %= 关系运算符 > < == >= <= != 逻辑运算符 && || ! & && 当等式左边为false,不会再计算等式右边 & 等式左右两边都会计算 内置运算符 is判断数据类型 bool test = 11 is object; 三元运算符 false ? 1 : 2; new运算符,创建实例 typeof运算符,获取类型的类型,Type result = typeof(int);
流程控制语句
if-else-if switch-case while do...while for foreach 可以使用跳转语句,控制流程 break 结束循环 continue 执行下次循环 return 立即结束 goto 跳转到指定位置,控制更加精细 在普通的循环中使用 int[] arr = new int[] { 1, 2, 3, 4, 5 }; foreach(int item in arr) { if(item == 3) { goto finished; } } finished: Console.WriteLine("执行完成"); Console.WriteLine("goto语句执行后的语句"); 在switch结构中使用 switch(3) { case 1: Console.WriteLine(1); goto default; case 2: Console.WriteLine(2); break; case 3: Console.WriteLine(3); goto case 2; default: Console.WriteLine("default"); }
索引器
索引器可以方便的访问类的成员 声明索引器 public class Demo { string[] str = new string[] { "aa", "bb", "cc" }; public string this[int i] { get => str[i];set => str[i] = value; } } 使用索引器 Demo test = new Demo(); test[1] = "dd"; string str = test[1];
c#基础升级
数组(不可变)
一维数组
声明数组 int[] arr = new int[5]; 声明一维数组,元素是数组 int[][] arr = new int[2][]; arr[0] = new int[2]; arr[1] = new int[3]; 初始化数组 int[] arr = new int[5] { 1, 2, 3, 4, 5 }; int[] arr = { 1, 2, 3, 4, 5 }; 数组排序 Array.Sort(arr); // 升序排序 数组反转 Array.Reverse(arr); // 反转排序
二维数组
声明二维数组 int[,] arr = new int[2, 3]; // 两行三列 二维数组的赋值 int[,] arr = new int[,] { { 1, 2 }, { 3, 4 } }; Console.WriteLine(arr[1, 0]); // 3 二维数组的遍历 int[,] arr = { { 1, 2 }, { 3, 4 } }; 使用for遍历 for(int i = 0; i < arr.GetLength(0); i++) { // 数组的每一行 for(int j = 0; j < arr.GetLength(1); j++) { // 数组每一行的每一个元素 Console.WriteLine(arr[i, j]); } } 使用foreach快速遍历 作用和for遍历一致 foreach(int item in arr) { Console.WriteLine(item); }
ArrayList(可以方便的操作数组)
声明
ArrayList arr = new ArrayList(); ArrayList arr = new ArrayList(5); ArrayList arr = new ArrayList(new int[] { 1, 2, 3, 4, 5 });
实例属性
列举部分属性 arr.Capacity; // 获取或者设置数组的容量 arr.Count; // 获取数组的元素个数 arr.IsFixedSize; // 数组是否固定大小
实例方法
arr.Add(6); // 添加元素 arr.Insert(5, 6); // 指定索引位置5插入元素6 arr.InsertRange(5, new int[] { 6, 7, 8 }); // 插入多个 arr.Clear(); // 清空 arr.Remove(5); // 删除指定元素 arr.RemoveAt(4); // 删除指定索引的元素 arr.RemoveRange(3, 2); // 从索引位置为3开始删除两个元素 int index = arr.IndexOf(3); // 查找指定元素3第一次出现的索引 int index = arr.LastIndexOf(3); // 查找指定元素3最后一次出现的索引 bool isHave = arr.Contains(3); // 是否包含指定元素 foreach(int item in arr){} // 遍历
List(泛型集合)
声明和ArrayList一样也有三种方式,不再赘述 List
list = new List () { 1, 2, 3, 4, 5 }; 方法使用(列举部分) list.Remove(5); // 指定元素删除 list.Insert(5, 6); // 指定索引5插入元素6 list.RemoveAll(item => item == 5); // 指定条件移除 list.RemoveAt(4); // 指定索引移除 list.Clear(); // 清空 string str = String.Join("|", list); // 拼接成字符串 int[] arr = list.ToArray(); // 转化成数组 foreach(int i in list){} // 遍历 list.ForEach(item => Console.Write(item)); // 遍历 属性 list.Count; // 数组长度 Hashtable(键值对集合)
声明 Hashtable obj = new Hashtable(); 属性 obj.Count; // 个数 方法 obj.Add("Name", "小叶"); // 添加元素 obj.Clear(); // 清空 obj.Remove("Name"); // 删除元素 bool isHave = obj.Contains("Name"); // 是否包含键 bool isHave = obj.ContainsValue("小叶"); // 是否包含值 遍历 foreach(DictionaryEntry entries in obj) { var key = entries.Key; // 键 var value = entries.Value; // 值 }
Dictionary
声明Dictionary实例 Dictionary
obj = new Dictionary (); 添加成员 obj.Add("a", "aaa"); 访问成员 obj["a"]; 遍历成员 根据键遍历 foreach(string item in obj.Keys) { } 根据值遍历 foreach(string item in obj.Values) { } 根据键值对遍历 foreach(KeyValuePair item in obj) { } 方法参数
一般传参 public static void Fn(int[] arr) { } Fn(new int[] { 1, 2, 3 }); 使用params修饰符 public static void Fn(params int[] arr) { } Fn(new int[] { 1, 2, 3 }); Fn(1, 2, 3); 两种调用方式等价 使用ref修饰符 将值类型的行为变成引用类型 public static void Fn(ref int num) { num = 111; } 调用,将num的值改成111了 int num = 1; Fn(ref num); 使用out修饰符 public static void Fn(out int num) { num = 111; } 调用,out的作用和ref类似,设计out的目的就是将一个变量在方法体内赋值,而设计ref的目的是在方法体内改变值 int num; Fn(out num);
异常处理
try { throw new Exception("aa"); }catch(Exception ex) { // 捕获异常 Console.WriteLine(ex.Message); throw; // 错误向方法的调用者抛出 } finally { // 始终都会执行 }
接口
基本使用
类只能继承一个抽象类,使用接口没有限制 接口中只定义声明,子类实现这些声明,并且是public 接口可以继承其他接口 接口成员不能使用权限修饰符 interface Test { string Name { get; set; } void Fn(); } public abstract class Demo1: Test { public string Name { get; set; } public abstract void Fn(); }
显示实现接口
当多个接口中有同名方法,这是就要使用显示接口了 显示实现接口类的成员不能使用任何的修饰符 public abstract class Demo1: Test1, Test2 { string Test1.Name { get; set; } string Test2.Name { get; set; } void Test1.Fn() { } void Test2.Fn() { } } interface Test1 { string Name { get; set; } void Fn(); } interface Test2 { string Name { get; set; } void Fn(); }
迭代器
迭代器就是foreach语句,迭代器能够操作的对象是实现了IEnumerator接口的对象 第一种遍历器 实现一个可迭代对象 public class Demo1 : IEnumerable { public IEnumerator GetEnumerator() { yield return "a"; yield return "b"; yield return "c"; yield break; } } 使用迭代器遍历 Demo1 test = new Demo1(); foreach(string item in test) { Console.WriteLine(item); } 第二种遍历器 实现一个可迭代对象 public class Demo : IEnumerable { string[] arr = new string[] { "a", "b", "c" }; public IEnumerator GetEnumerator() { return new Demo1(arr); } } 实现一个枚举类 public class Demo1 : IEnumerator { string[] arr; int index = -1; public Demo1(string[] arr) { this.arr = arr; } public object Current => arr[index]; public bool MoveNext() { if(index+1 < arr.Length) { index++; return true; } return false; } public void Reset() { index = -1; } } 使用foreach迭代 Demo demo = new Demo(); foreach(var item in demo) { Console.WriteLine(item); }
文件流
File类
文件操作的一些静态方法 复制文件 File.Copy("F:/学习实验区/c#/demo.txt", "F:/学习实验区/c#/copy/demo.txt"); 创建并覆盖文件 File.Create("F:/学习实验区/c#/demo.txt"); 删除文件 File.Delete("F:/学习实验区/c#/demo.txt"); 是否存在文件 File.Exists("F:/学习实验区/c#/demo1.txt"); 文件剪切 File.Move("F:/学习实验区/c#/demo1.txt", "F:/学习实验区/c#/demo2.txt"); 将demo.txt文件内容使用demo1.txt覆盖,并且将demo.txt文件备份成demo3.txt File.Replace("F:/学习实验区/c#/demo1.txt", "F:/学习实验区/c#/demo.txt", "F:/学习实验区/c#/demo3.txt"); 将文件读成字节数组 byte[] bytes = File.ReadAllBytes("F:/学习实验区/c#/demo.txt"); 将文件的内容读成字符串数组,一行就是一个元素 string[] arr = File.ReadAllLines("F:/学习实验区/c#/demo.txt"); 将文件读成字符串 string str = File.ReadAllText("F:/学习实验区/c#/demo.txt"); 创建文件并一行一行写入数据 File.WriteAllLines("F:/学习实验区/c#/demo4.txt", new string[] { "a", "b", "c" }); 创建文件并写入文本 File.WriteAllText("F:/学习实验区/c#/demo.txt", "你好"); 获取文件或者目录的创建时间 DateTime time = File.GetCreationTime("F:/学习实验区/c#/demo.txt"); 获取文件或者目录上次写入的时间 DateTime time = File.GetLastWriteTime("F:/学习实验区/c#/demo.txt"); 设置文件的创建日期 File.SetCreationTime("F:/学习实验区/c#/demo.txt", DateTime.Now); 设置文件的上次访问时间 File.SetLastAccessTime("F:/学习实验区/c#/demo.txt", DateTime.Now); 设置文件的上次写入时间 File.SetLastWriteTime("F:/学习实验区/c#/demo.txt", DateTime.Now); 文件流一般处理 写数据 using(FileStream fs = File.Open("F:/学习实验区/c#/demo.txt", FileMode.Append, FileAccess.Write)) { using(StreamWriter sw = new StreamWriter(fs)) { sw.WriteLine("哈哈"); // 向文件中写入一行数据 sw.WriteLine("你好"); } } 读数据 using(FileStream fs = File.Open("F:/学习实验区/c#/demo.txt", FileMode.Open, FileAccess.Read)) { using(StreamReader sr = new StreamReader(fs)) { string str = sr.ReadToEnd(); Console.WriteLine(str); } } 文件流简化处理 快速创建或打开一个文件,并写入数据 using(StreamWriter sw = File.CreateText("F:/学习实验区/c#/demo.txt")) { sw.WriteLine("哈哈哈哈哈哈"); } 快速打开一个文件所有数据 using(FileStream fs = File.OpenRead("F:/学习实验区/c#/demo.txt")) { using(StreamReader sr = new StreamReader(fs)) { Console.WriteLine(sr.ReadToEnd()); } } 文件流分段读取 一次性全读 using(FileStream fs = File.OpenRead(@"F:\学习实验区\c#\demo.txt")) { byte[] bytes = new byte[fs.Length]; fs.Read(bytes, 0, bytes.Length); // 将数据读取到bytes字节数组中 Console.WriteLine(Encoding.UTF8.GetString(bytes)); } 分小段一点点读 using(FileStream fsr = File.OpenRead(@"F:\学习实验区\c#\demo.txt")) { using(FileStream fsw = File.OpenWrite(@"F:\学习实验区\c#\test.txt")) { byte[] bytes = new byte[1]; int r = 0; while((r = fsr.Read(bytes, 0, bytes.Length)) > 0) { fsw.Write(bytes, 0, r); // 追加写入数据 // -----读取进度获取----- double percent = (fsw.Position * 100) / fsr.Length; Console.WriteLine(percent); } } }
FileInfo类
使用FileInfo类可以对多次重复操作相同文件简化操作 列举部分 创建实例 FileInfo fi = new FileInfo("F:/学习实验区/c#/demo.txt"); 创建StreamWriter实例 StreamWriter sw = fi.AppendText(); 获取文件创建时间 DateTime time = fi.CreationTime; 获取上次访问时间 DateTime time = fi.LastAccessTime; 获取上次写入时间 DateTime time = fi.LastWriteTime; 获取父目录实例 DirectoryInfo di = fi.Directory; 获取目录路径 string path = fi.DirectoryName; 文件是否存在 bool isHave = fi.Exists; 获取文件扩展名 string str = fi.Extension; 获取文件完整路径 string str = fi.FullName;
Directory类
Directory类用来操作文件夹 创建文件夹 Directory.CreateDirectory("F:/学习实验区/c#/demo"); 删除文件夹 Directory.Delete("F:/学习实验区/c#/demo"); 文件是否存在 bool isHave = Directory.Exists("F:/学习实验区/c#/demo"); 目录剪切 Directory.Move("F:/学习实验区/c#/demo", "F:/学习实验区/c#/demo1"); 获取目录创建时间 DateTime time = Directory.GetCreationTime("F:/学习实验区/c#/demo"); 设置基路径 Directory.SetCurrentDirectory("F:/学习实验区/c#/demo"); Directory.GetFiles("./"); 获取指定路径的父目录 Directory.GetParent("F:/学习实验区/c#/demo"); 获取指定路径的文件 string[] str = Directory.GetFiles("F:/学习实验区/c#/demo"); 获取目录的子目录 string[] arr = Directory.GetDirectories("F:/学习实验区/c#/demo"); 获取盘符 string str = Directory.GetDirectoryRoot("F:/学习实验区/c#/demo");
DirectoryInfo类
使用DirectoryInfo类可以对多次重复操作相同目录简化操作 创建实例 DirectoryInfo di = new DirectoryInfo("F:/学习实验区/c#/test"); 获取创建时间 di.CreationTime 获取绝对路径 di.FullName 获取文件名 di.Name
压缩文件
using(FileStream fsr = File.OpenRead(@"F:\学习实验区\c#\demo.txt")) { using(FileStream fsw = File.OpenWrite(@"F:\学习实验区\c#\demo.zip")) { using(GZipStream gs = new GZipStream(fsw, CompressionMode.Compress)) { byte[] bytes = new byte[1024]; // 内存中缓存的数据大小 int len = 0; while((len = fsr.Read(bytes, 0, bytes.Length)) > 0) { gs.Write(bytes, 0, len); } } } }
解压文件
using(FileStream fsr = File.OpenRead(@"F:\学习实验区\c#\demo.zip")) { using(GZipStream gs = new GZipStream(fsr, CompressionMode.Decompress)) { using(FileStream fsw = File.OpenWrite(@"F:\学习实验区\c#\demo1.txt")) { byte[] bytes = new byte[1024]; int len = 0; while ((len = gs.Read(bytes, 0, bytes.Length)) > 0) { fsw.Write(bytes, 0, len); } } } }
加密文件
using(FileStream fsr = File.OpenRead(@"F:\学习实验区\c#\demo.txt")) { using(FileStream fsw = File.OpenWrite(@"F:\学习实验区\c#\test.txt")) { byte[] bytes = new byte[1024]; int len = 0; while((len = fsr.Read(bytes, 0, bytes.Length)) > 0) { // 加密逻辑,再次运行此加密逻辑可实现解密 for(int i = 0; i < len; i++) { bytes[i] = (byte)(byte.MaxValue - bytes[i]); } fsw.Write(bytes, 0, len); } } }
路径操作
string path = @"F:\学习实验区\c#\demo.txt"; 获取路径的文件名 string str = Path.GetFileName(path); 获取文件扩展名 string str = Path.GetExtension(path); 获取文件名不带扩展名 string str = Path.GetFileNameWithoutExtension(path); 获取文件名以外的目录部分 string str = Path.GetDirectoryName(path); 设置文件扩展名(内存) string str = Path.ChangeExtension(path, ".exe"); 返回随机文件名 string str = Path.GetRandomFileName(); 也可以使用 DateTime.Now.ToFileTime() 路径合并 string str = Path.Combine(path, path1); 获取当前项目目录下文件的绝对路径 string str = Path.GetFullPath("demo.txt"); 返回系统临时目录绝对路径 string str = Path.GetTempPath(); 创建临时文件并且返回文件绝对路径 string str = Path.GetTempFileName();
序列化
Json序列化
需要添加System.Web.Extensions引用 JavaScriptSerializer js = new JavaScriptSerializer(); 可以序列化类的属性 string s = js.Serialize(new Demo());
XML序列化
可以将类序列化成xml文件,[XmlIgnore]无视某个属性 XmlSerializer xml = new XmlSerializer(typeof(Demo)); using(FileStream fsw = File.OpenWrite(@"F:\学习实验区\c#\demo.xml")) { xml.Serialize(fsw, new Demo()); }
二进制序列化
序列化的类需要添加[Serializable],使用[NonSerialized]无视属性 BinaryFormatter bf = new BinaryFormatter(); using(FileStream fs = File.OpenWrite(@"F:\学习实验区\c#\demo.bin")) { bf.Serialize(fs, new Demo()); }
二进制反序列化
反序列化同样需要[Serializable] BinaryFormatter bf = new BinaryFormatter(); using(FileStream fsr = File.OpenRead(@"F:\学习实验区\c#\demo.bin")) { Demo obj = bf.Deserialize(fsr) as Demo; }
正则表达式
元字符
. -> 除了\n以外的任意单个字符 [] -> 字符组,多个字符任意一个 a-z -> a到z任意一个字符 | -> 或,如 a(x|y)b,z|food 表示 z 或者 food,或优先级最低 * -> 表示前面一个字符可以出现任意多次 + -> 表示前面一个字符可以出现一次或者多次 ? -> 表示前面一个字符可以出现零次或者一次 {n} -> 表示前面一个字符可以出现指定的次数 {n,} -> 表示前面一个字符可以出现至少n次 {n,m} -> 表示前面一个字符可以出现至少n次,至多m次 ^ -> 表示开头 $ -> 表示结尾 [^] -> 表示取反 \d -> 0-9任意数字 \D -> 0-9以外的其他字符 \w -> a-zA-Z0-9任意字符 \W -> a-zA-Z0-9以外的任意字符 \s -> 表示不可见字符 \S -> 表示所有可见字符
正则基本使用
string str = "这是2222什么23334这是啥9878我也不5555知道0987678"; 创建正则实例 Regex regex = new Regex(@"\d"); 是否匹配 bool isMatch = Regex.IsMatch("666", "[0-9]{3}"); 提取第一个匹配的元素 Match result = Regex.Match(str, @"\d+"); 提取所有匹配的元素 MatchCollection result = Regex.Matches(str, @"\d+"); 替换匹配的元素 string result = Regex.Replace(str, @"\d+", string.Empty); string result = Regex.Replace("10/11/2017", @"(\d+)/(\d+)/(\d+)", "$3-$2-$1"); 匹配分组 Match result = Regex.Match("aaa333", @"([a-z]+)(\d+)"); Console.WriteLine("{0}-{1}-{2}", result.Groups[0], result.Groups[1], result.Groups[2]); 和split结合使用 string[] result = Regex.Split("this is me", @"\s");
贪婪模式
正则表达式限定符默认按照多的匹配 贪婪 Match match = Regex.Match("abbbb", "ab+"); // match.Value -> abbbb 取消贪婪 Match match = Regex.Match("abbbb", "ab+?"); // match.Value -> ab
英文单词边界
作用就是限定一个单词 string result = Regex.Replace("a aa bv", @"\baa\b", "ccc");
环视
?<= 表示向做看 ?= 表示向右看 Match result = Regex.Match("this is me", @"(?<= )is(?= )");
反向引用
string result = Regex.Replace("aaabbbccc", @"(.)\1+", "$1"); // abc
使用委托方法
string result = Regex.Replace("aaabbbccc", @"(a{3})", Fn); public static string Fn(Match match) { return match.Groups[1].Value + "-"; }
Type的使用
通过获取Type可以方便的获取类的相关参数 通过实例获取Type Demo demo = new Demo(); Type tp = demo.GetType(); 通过类本身获取Type Type tp = typeof(Demo); 获取父类的Type Type tp = tp.BaseType; 获取类的所有public字段 FieldInfo[] t = tp.GetFields(); Console.WriteLine(t[0].Name); 获取类的所有public属性 PropertyInfo[] t = tp.GetProperties(); 类似的还有 GetMethods,GetMembers...
反射
反射的作用就是通过不导入程序集的方式,获取程序中的内容 获取程序集 Assembly asb = Assembly.LoadFile(@"test.exe"); 获取所有的类 Type[] tps = asb.GetTypes(); 获取所有的public类 Type[] tps = asb.GetExportedTypes(); 获取指定类 Type tp = asb.GetType("ConsoleApp1.Demo"); 获取指定public方法 MethodInfo mi = tp.GetMethod("Fn"); 获取实例对象 无参数构造函数 object obj = Activator.CreateInstance(tp); 调用方法 tp.Invoke(obj, new object[] { "aaa" }); 有参数构造函数 var obj = tp.GetConstructor(new Type[] { typeof(string) }); 调用方法 object result = obj.Invoke(new object[] { "aaa" });
线程
线程的使用
Thread tr = new Thread(() => { while (true) { Console.WriteLine("新创建的线程"); Thread.Sleep(1000); } }); // 创建线程 tr.Start(); // 运行线程 tr.IsBackground = true; // 设置线程为后台线程 tr.Priority = ThreadPriority.Highest; // 设置线程优先级 tr.Abort(); // 结束线程 tr.Join(2000); // 单独执行此线程2秒,然后和主线程一起执行 tr.ManagedThreadId // 线程的id Thread.CurrentThread.ManagedThreadId // 主线程id
线程池的使用
线程池默认是后台线程,速度更快 ThreadPool.QueueUserWorkItem(a => { Console.WriteLine(a); }, "你好"); 获取最大线程数,和实际最大线程数 int a, b; ThreadPool.GetMaxThreads(out a, out b); Console.WriteLine(a + "|" + b);
socket编程(端口可以使用49152到65535)
Socket sk = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); // 创建socket对象 IPAddress ia = IPAddress.Parse("192.168.31.198"); // 根据ip地址,创建IPAddress实例 IPEndPoint ie = new IPEndPoint(ia, int.Parse("8881")); sk.Bind(ie); // 将ip和端口使用socket对象绑定 sk.Listen(10); // 开始监听,允许同时连接10个 while (true) { Socket proxSk = sk.Accept(); // 等待客户端的接入,阻塞当前线程,返回新的Socket对象 Console.WriteLine(proxSk.RemoteEndPoint.ToString()); // 获取远程连接客户端的信息 byte[] bytes = Encoding.UTF8.GetBytes(DateTime.Now.ToString()); proxSk.Send(bytes, 0, bytes.Length, SocketFlags.None); // 给连接的客户端发送信息 proxSk.Close(); //proxSk.Shutdown(SocketShutdown.Both); // 关闭接受和连接的socket实例 }
async和task的用法
简单的异步任务 public async Task
Fn(string str) { return await Fn1(str); } Task Fn1(string str) { Thread.Sleep(3000); return Task.FromResult(str); } 使用上面创建的任务 Program test = new Program(); Console.WriteLine("1"); var result = test.Fn("aa"); // 执行任务,等待任务执行完成 Console.WriteLine("2"); Console.WriteLine(result.Result); // 获取任务的返回值 Console.WriteLine("3"); Console.ReadKey(); 执行结果如下 1 // 立即执行 2 // 等待2秒执行 aa 3
c#实用应用
MD5加密
任何一个东西都可以用md5生成一个唯一不可逆固定长度的字符串密码,相同的东西md5密码都是一样的 字符串生成md5 string str = "aaabbbccc"; StringBuilder result = new StringBuilder(); using(MD5 sc = MD5.Create()) { byte[] bytes = sc.ComputeHash(Encoding.UTF8.GetBytes(str)); for(int i = 0; i < bytes.Length; i++) { result.Append(bytes[i].ToString("x2")); } } 文件生成md5 StringBuilder result = new StringBuilder(); using(MD5 sc = MD5.Create()) { using(FileStream fs = File.OpenRead(@"F:\学习实验区\c#\demo.txt")) { byte[] bytes = sc.ComputeHash(fs); for(int i = 0; i < bytes.Length; i++) { result.Append(bytes[i].ToString("x2")); } } }
excel操作(使用NPOI,下载地址http://npoi.codeplex.com/)
写入数据到excel文件
IWorkbook Iwb = new HSSFWorkbook(); // 创建工作薄 ISheet Is = Iwb.CreateSheet("TestSheet"); // 创建一个工作表 IRow row = Is.CreateRow(0); // 创建第0行 row.CreateCell(0).SetCellValue("yejiawei"); // 创建行的第0个单元格并写入值 row.CreateCell(1).SetCellValue(CellType.Blank); // 创建空的单元格 using(FileStream fs = File.OpenWrite("test.xlsx")) { Iwb.Write(fs); // 写入文件 }
读取excel
using(FileStream fs = File.OpenRead("test.xlsx")) { IWorkbook wk = new HSSFWorkbook(fs); // 将excel数据读取到wk中 for(int i = 0; i < wk.NumberOfSheets; i++) { ISheet sheet = wk.GetSheetAt(i); // 获取工作表 for(int j = 0; j <= sheet.LastRowNum; j++) { IRow row = sheet.GetRow(i); // 获取行 for(int k = 0; k <= row.LastCellNum; k++) { ICell cell = row.GetCell(k); // 获取单元格 Console.WriteLine(cell); } Console.WriteLine(); } } }
中文转拼音
安装插件
下载地址https://www.microsoft.com/zh-cn/download/details.aspx?id=15251 安装CHSPinYinConv.msi软件,根据安装路径将ChnCharInfo程序集添加到程序中来
将中文转成拼音
string str = "你好"; StringBuilder sb = new StringBuilder(); for(int i = 0; i < str.Length; i++) { ChineseChar cn = new ChineseChar(str[i]); // 将每一个中文字转成ChineseChar实例 if(cn.Pinyins.Count > 0) { string py = cn.Pinyins[0]; // 获取中文对应的拼音 sb.Append(py.Substring(0, py.Length - 1)); } } Console.WriteLine(sb);