本文介绍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<T>: Test1<T> where T: Test2 { }
public interface Test1<T>: Test2 { }
public interface Test2 { }
泛型方法 public void Fn<T>(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<int> 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<string> fn = (str) => { };
public delegate void Dlg<T>(T str);
内置委托类型
Action类型,没有返回值 Action<string> Fn = (str) => { };
Func类型,有返回值 Func<int, string, string> 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<int, int> 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<int> list = new List<int>() { 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<string, string> obj = new Dictionary<string, string>();
添加成员 obj.Add("a", "aaa");
访问成员 obj["a"];
遍历成员
根据键遍历 foreach(string item in obj.Keys) { }
根据值遍历 foreach(string item in obj.Values) { }
根据键值对遍历 foreach(KeyValuePair<string, string> 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<string> Fn(string str)
{
return await Fn1(str);
}
Task<string> 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);