本文介绍c#的实用知识点

写在前面(通识)

  1. vs常用快捷键

    F5 调试运行程序
    ctrl F5 不调试运行程序
    F11 逐条语句调试
    F10 逐过程调试程序
    注释快捷键 ctrl + k + c
    代码格式化 ctrl + A + k + F
    强制智能提示 ctrl + J
  2. 面相对象语言三大特性

    封装性,重复代码共用
    继承性,类,接口等的继承
    多态性,不同的子类调用父类的方法,执行效果不一样
  3. c#中的访问修饰符

    private 本类内部可以使用
    protected 本类内部和子类内部可以使用
    internal 当前程序集中可用
    protected internal 当前程序集中,并且是的当前类和子类内部可用
    public 访问无限制
    类成员不写访问修饰符默认是private
    类不写访问修饰符默认是internal
    命名空间中定义的成员只能是public或者internal
  4. 属性和字段

    private string _str = "aaa"; // 字段
    internal int num { get; set; } // 属性
  5. 方法的重载

    方法名相同,只要参数类型、个数或者顺序不同时就认为是不同的函数
  6. 泛型

    泛型是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) { }
  7. 测量代码的运行时间

    Stopwatch watch = new Stopwatch();
    watch.Start();
    // ...
    watch.Stop();
    TimeSpan time = watch.Elapsed;
  8. 生成guid

    String str = Guid.NewGuid().ToString();
  9. 嵌套引号的写法

    string str = @"aaaa""aa""";
  10. 扩展方法

    指定任意一个顶级静态类就可以定义任意类型的扩展方法
    定义扩展方法
    public static class Demo
    {
    public static void Fn(this int a)
    {
    Console.WriteLine("aa");
    }
    }
    使用扩展方法
    int num = 222;
    num.Fn();

c#基础

  1. 变量

    声明变量 string str1 = 'aa', str2
    添加?号,表示可以赋值null,int? i = 1; 等价于 Nullable<int> a = null;
    static string str;
    string str1;
    public void Fn()
    {
    str = "aaa";
    str1 = "bbb";
    }
    在代码块中定义的变量,只在代码块中有效
    嵌套的子作用域中不能有父作用域中的同名变量
  2. 常量

    const string str2 = "aaa";
    在声明的时候必须赋值,并且后续无法修改
  3. 数据类型

    整数类型
    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
  4. 表达式和运算符

    算数运算符 + - * / %
    赋值运算符 = += -= /= *= %=
    关系运算符 > < == >= <= !=
    逻辑运算符 && || ! &
    && 当等式左边为false,不会再计算等式右边
    & 等式左右两边都会计算
    内置运算符
    is判断数据类型 bool test = 11 is object;
    三元运算符 false ? 1 : 2;
    new运算符,创建实例
    typeof运算符,获取类型的类型,Type result = typeof(int);
  5. 流程控制语句

    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");
    }
  6. 索引器

    索引器可以方便的访问类的成员
    声明索引器
    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#基础升级

  1. 数组(不可变)

    声明数组 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);
    }
  2. 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){} // 遍历
  3. 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; // 数组长度
  4. 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; // 值
    }
  5. 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) { }
  6. 方法参数

    一般传参
    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);
  7. 异常处理

    try
    {
    throw new Exception("aa");
    }catch(Exception ex)
    {
    // 捕获异常
    Console.WriteLine(ex.Message);
    throw; // 错误向方法的调用者抛出
    }
    finally
    {
    // 始终都会执行
    }
  8. 接口

    类只能继承一个抽象类,使用接口没有限制
    接口中只定义声明,子类实现这些声明,并且是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();
    }
  9. 迭代器

    迭代器就是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);
    }
  10. 文件流

    文件操作的一些静态方法
    复制文件 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 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.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 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);
    }
    }
    }
  11. 路径操作

    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();
  12. 序列化

    需要添加System.Web.Extensions引用
    JavaScriptSerializer js = new JavaScriptSerializer();
    可以序列化类的属性
    string s = js.Serialize(new Demo());
    可以将类序列化成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;
    }
  13. 正则表达式

    . -> 除了\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 + "-";
    }
  14. 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...
  15. 反射

    反射的作用就是通过不导入程序集的方式,获取程序中的内容
    获取程序集 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" });
  16. 线程

    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);
  17. 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实例
    }
  18. 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#实用应用

  1. 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"));
    }
    }
    }
  2. excel操作(使用NPOI,下载地址http://npoi.codeplex.com/)

    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); // 写入文件
    }
    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();
    }
    }
    }
  3. 中文转拼音

    下载地址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);
05-11 17:20