首页 > 代码库 > .NET基础笔记(C#)

.NET基础笔记(C#)

     闲着没事就把以前学习时的笔记拿出来整理了一下,个人感觉有点用,就想拿出来跟园友共享一下。有些基础性的内容比如基本概念、语法什么的就不发了。

内容:1、构造方法(函数) 2、继承   3、访问修饰符  4、静态和非静态  5、隐藏基类方法  6、重写基类方法  7、抽象方法  8、接口  9、多态和接口  10、值类型与引用类型  11、ref和out  12、类型转换  13、异常处理  14、string字符串处理  15、string常用方法  16、StringBulider  17、File类读取文件  18、文本高亮 19、泛型集合List<T>和Dictionary<TKey,TValue>  20、装箱和拆箱  21、读写数据(Ststem.IO)  22、文件流(FileStream)  23、StreamReader和StringWriter  24、File和Directory  25、序列化  26、Directory类的静态方法  27、正则表达式  28、Regex类  29、字符串提取  30、贪婪模式  31、字符串替换  32、byte数组和字符串的转换  33、委托  34、多播委托  35、事件  36、委托和事件的区别  37、var类型  38、扩展方法  39、XML

1、构造方法(函数):在对象创建的时候初始化字段

    public 类名([参数])

    {

        //执行内容,如this.name=name;(有参数)

    }

  构造方法没有返回值,void也不写

  创建对象时如果没写构造方法系统默认有一个无参的构造方法,一旦用户添加了构造方法,原来默认的无参的构造方法就没有了

  默认初始值为:int:0;  bool:false;  string:null;   char:‘\0’; 

  构造方法的作用:初始化字段(实例化类的时候传入初始值)

  调用构造方法:

    1)除了new对象的时候,不能直接使用构造方法名调用构造方法

    2)this调用构造方法(难点)(一个构造方法调用另一个构造方法)

   为了解决代码的重用才使用this调用构造方法

   当一个类中出现多个构造方法重载的时候,同时构造方法都需要为字段赋初值

   使用this代表当前类的构造方法来使用,根据this后面的参数决定使用哪一个重载

    public 构造方法名():this([参数])

    {

        //代码

    }

如:  

 public Person(string name, int age, string sex)

        {

            this.name = name;

            this.age = age;

            this.sex = sex;

        }

        public Person(string name)

            : this(name, 0, null)

        {

        }

2、继承:

  多个类都有共同的字段、属性或方法时,可以使用继承

  需要写一个学生类、一个老师类、一个校长类

  事先写一个Person类,在写其他类的时候继承这个类,Person叫父类,继承他的叫子类

  特征:子类既有父类的非私有字段、属性和方法,又有子类独有的字段、属性和方法

  一个子类只能有一个父类,一个父类可以有若干个子类

    [访问修饰符] class 子类名:父类名

子类调用父类构造方法的问题(难点)

  ->构造方法的执行顺序:先父类再子类

  ->关于构造方法的重载:如果不声明调用哪个构造方法默认执行无参的构造方法

  避免错误的方法:

->为父类提供无参构造方法

->在子类中指定调用父类有参的构造方法

注意:父类除构造函数(方法)之外的非私有成员都可以被子类所继承,构造方法不可以被继承,但可以被调用,调用方法: [访问修饰符] 构造方法名([参数]):base(父类构造方法的参数)

public Teacher():base("狗蛋”,20,"女")

        {        }    //直接赋值,实例化对象时不需再赋值

 

 public Teacher(string name, int age, string sex)

            : base(name, age, sex)

        {

            this.className = "C#";//老师类独有的字段

        }   //继承传递参数,实例化对象时需要赋值

  base关键字用于显示声明要调用父类的哪个构造方法,是调用,不是声明不是继承

  注意:为避免继承父类时因为构造方法出错,应该为每个声明的类都手动添加一个无参的构造方法

3、访问修饰符

  public 最公开的,所有地方都可以访问

  private 最隐蔽的,只有本类可以访问

  protected 可以在当前类和子类中被访问

  internal 只能在本项目中被访问

  C#中规定类的访问修饰符只能是public 和internal 

  类中成员的访问修饰符除以上四种以外还有个(protected internal)即可以在当前类、子类和本项目中被访问  

4、静态和非静态

  静态使用static标记

静态成员

  ->如何定义静态成员

       在成员前加static关键字

  ->如何使用静态成员 

静态成员不能由实例对象调用,只能用类名调用

使用静态成员,不需要实例化对象

静态成员会在整个应用程序退出时才释放资源,所以这个变量可以在整个程序中共享数据,可用于窗体间传递参数

注意:静态变量过多会占用系统内存,因此声明静态变量时要多加考虑

Main方法是静态的,所以它只能调用静态的方法或参数,若想调用非静态方法,必须new一个类的实例,用实例名调用。

静态类

  1)什么情况下要将一个类标记为静态类?

      一般情况是,当这个类是一个工具类,里面都是方法,为了让用户调用的时候方便,不需要实例化对象,这时可以将该类标记为static类,此时该类中只能包含静态成员,不能包含实例成员,比如Convert、Console、Read、ReadInt等

  2)什么情况下需要在一个普通类中编写一个静态成员,而这个类不能标记为static?

      当这个类需要被实例化的时候,如果这个类中有一个成员是所有对象要共享的数据,这时可以将该类中的这个成员标记为静态的,但是这个类还是一个非静态类

  3)静态类不能被实例化,不能被继承

  4)由于静态成员会在整个程序退出时才释放资源,所以尽量避免写静态字段或静态属性,最好只写静态方法

  5)静态类中可以有静态成员和实例成员,因为其不能被实例化,所以静态类中的实例成员无法被调用,也就没有任何意义了

  6)静态类不能被继承,也不能继承自其它类,只能继承自Object类

静态构造函数

  1)静态构造方法(或字段)在第一次访问这个类的时候执行,并且只执行一次

  ->静态构造方法与静态类的生命周期(了解)

静态成员属于该类的所有对象。实例成员只属于当前实例

静态类的生命周期:第一次访问类的时候创建,程序结束时才释放

 

5、隐藏基(父)类方法(了解,用的不多)

  当子类和父类有相同名字的方法时,实例化子类调用该方法为子类内的方法,把子类转换成父类后再调用该方法就是父类内的方法了

  为了代码规范化,如要有意隐藏父类的方法,要在子类的同名方法前加new关键字,用于   提醒这里是要隐藏父类方法

//隐藏基类方法

    class MyBase

    {

        public void Func()

        {Console.WriteLine("我是父类的方法);}

    }

    class MySub : MyBase

    {

        public new void Func() //在方法前用new关键字提醒这里是有意隐藏父类方法

        {Console.WriteLine("我是子类的方法);}

    }

    class Program

    {

        static void Main(string[] args)

        {

            MySub ms = new MySub();

            ms.Func();

           // ((MyBase)ms).Func();

            MyBase my = ms;

            my.Func();

            Console.ReadKey();

        }

    }

6、重写基(父)类方法(多态的体现)

  在父类方法前加virtual(虚方法),表示这个方法可以被重写

  在子类方法前加override,表示重写基类方法,子类如果不写override就是隐藏

 当子类和父类有相同名字的方法时,实例化父类调用的是父类的方法,将子类赋值给父类后,父类调用的就是子类的方法了

  一旦父类的方法在子类中被重写,除了实例化父类调用以外,子类或者子类的子类调用时都是调用的最新的那个子类重写方法

  此时base和this就有区别了:

  如果子类和父类中有名字相同的方法,那么base.Func()就代表父类的方法,this.Func()代表当前类的方法

  如果不希望子类再重写,需要在子类方法前加sealed

//重写基类方法

    class USB

    {

        public virtual void Usb() //virtual关键字表示父类方法可以被重写′

 

        {Console.WriteLine("我是父类的方法);}

    }

    class Phone:USB

    {

        public override void Usb()//override关键字表示这个方法重写父类的方法

 

        {Console.WriteLine("手机");}

    }

    class Mp3:USB

    {

        public override void Usb()

        {Console.WriteLine("MP3");}

    }

    class Program:USB

    {

        static void Main(string[] args)

        {

            Console.WriteLine("输入数字");

            USB usb = new USB();

            switch (Console.ReadLine() )

            {

                case "1":

                    usb = new Phone();

                    break;

                case "2":

                    usb = new Mp3();

                    break;

                default:

                    break;

            }

            usb.Usb();

            Console.ReadKey();

        }

}

隐藏看类型,重写只管新

7、抽象方法

  方法前加abstract,圆括号后加分号

  [public]  abstract  void 方法名(参数);

  抽象方法所在的类也必须是抽象的,不能被实例化,类前加abstract  

  abstract  class  类名

  抽象成员必须出现在抽象类中,但抽象类中可以有抽象成员和实例成员

  抽象类的抽象方法默认可以且必须被重写,所以需要重写时不需要再写virtual,直接在子类方法中写override关键字即可重用

  抽象类的成员:方法、属性、索引、事件

  注意:抽象类中的所有抽象方法在子类中必须要被重写,如果子类中不重写,那这个子类也必须声明成抽象类,但此时子类也就不能被实例化了

8、接口

  [访问修饰符] interface 接口名(接口一般以I开头,I+行为+able)

  {

    //接口成员,一定是抽象成员

  }

  成员的定义:与抽象成员的写法一样(没有关键字),没有执行体(方法体);没有访问修符;

  实现:子类“继承”自接口,接口可以看做是子类的“干爹”,补全抽象的方法

  调用:和类的使用方法一样,要使用接口中的方法,则将对象赋值给接口变量

  多态:和抽象类的实现一样,接口的多继承使得多态的实现变得灵活

  子类继承父类,子类实现(继承)接口:class 子类名:父类名,接口名 或class 类名:接口名

  个人理解:接口就是把类中的一种特定的功能性方法封装成接口(如开车、飞等),声明类的时候可以继承这个接口,就拥有了这个接口所特有的功能,就可以在类中实现这个功能,那实现后的这个功能就成为了这个类的一个功能性方法。用接口可以实现多继承,一个类可以继承多个接口,也就拥有了多个功能性方法,声明类的时候可以选择性的继承,比如定义老师的时候可以继承说话、吃饭、教课等接口,定义司机的时候又可以继承说话、吃饭、开车等接口

//定义一个接口,表示Driving功能

interface IDrivable

    {

        void Driving();

    }

class Teacher:Person,IDrivable//继承父类和接口

    {

        public Teacher(string name, int age, string sex):base(name,age,sex)

        {//继承父类的构造方法

        }

        public void Driving()//实现接口的Driving功能

        {

            Console.WriteLine("我会开车");

        }

        public override void ShowMe()//重写父类方法

        {

            Console.WriteLine("我是老师");

        }

    }

static void Main(string[] args)

        {

            Teacher tea = new Teacher("张三", 18, "男");

            tea.ShowMe();//调用重写后的方法

            tea.Driving();//调用实现后的接口方法

            Console.ReadKey();

        }

9、多态和接口

  接口是可以多继承的,多继承同时会引起方法的重名

  为避免重名,显示实现接口:

返回值 接口名.方法名(参数)

   {

        //方法体

   }

显示实现接口的这个方法只能由接口变量进行调用

现阶段记住接口使用的语法,然后将接口直接当做抽象类使用(初学阶段)

10、值类型和引用类型

  变量可以看做是一个数据

  值类型,是一个存储数据的容器,这个数据就是这个类型表示的数据

  引用类型,也是一个容器,但是这个容器存储对象的一个引用(地址),真正的数据在另一块内存中,就相当于是一个指向数据的快捷方式

  值类型存储在栈里面,引用类型存储在栈和堆里面(堆里存储的是真实数据,栈里面存储的是真实数据在堆里面的内存地址)

  值类型和引用类型会涉及到传参和赋值时的不同,要注意区分

 

  委托(delegate)也是引用类型

  值类型赋值赋的是真实的值,引用类型赋值赋的是地址

  引用类型是一个变量,两个快捷方式,修改一个快捷方式会影响另一个快捷方式;值类型是一个变量和一个复制后的变量,修改一个变量不会影响另一个变量

  所有的值类型都继承自System.ValueType类

11、引用传递:ref和out

  使用ref或out就可以实现将引用传递过来

  定义方法时,在参数类型前加ref或out

  调用方法时,在参数前加ref或out

  注:定义变量时不需要加ref或out

  当一个变量传递给一个方法时,总是复制一份自己的数据,赋值给方法,那么方法中执行的变量就与方法外的那么变量没有关系了,方法内修改变量值,也不会影响方法外的那个变量。有时会要求多个返回操作(方法中的变量有多个在方法外需要使用),此时可以使用ref标记参数,此时在方法中使用的这个变量与方法外的变量就是同一个变量了,相当于可以使用参数实现返回值

    out与ref的作用与使用方法相同

  out 必须在方法中赋值

  ref必须在方法外赋值

12、类型转换

  隐式类型转换:兼容类型,小转大

  显式类型转换:即强制类型转换  (转换后的类型)要转换的变量

  Convert类型转换:系统封装的方法,Convert.ToInt32()、Convert.ToDouble()等

  其他转换方法:int.Parse()、int.TryParse()(第一个参数表示待转换的字符串,第二个out(result)参数表示数字转换成功后的变量,如果转换失败,返回false,为out result赋值为0)

  用int.TryParse()代替try-catch,释放内存,提高性能

  不止int类型有TryParse,所有基本类型都有TryParse,用法都一样

13、异常处理(try-catch、try-catch-finally、try-finally)

  Exception是一个专门用来封装异常的一个类型

  两个属性:message(异常说明文本)和stackTrace(异常抛出的顺序)

  抛出异常:throw Exception的一个对象(new一个对象)

  从出现异常的try向上依次抛出(方法之间依次调用),直至处理,找到最近的catch捕获异常(用于try-finally语句,catch块在方法以外,执行顺序是try-finally再向上寻找catch执行异常捕获)

Finally用于释放资源,finally{},程序执行完try-catch后会再执行finally语句块

Finally总会执行,即使try-catch中有return

           try

            {  }

            catch (Exception ex)

            { throw; }

            finally

            {  }

Try-finally用处不多,用于释放资源

使用异常会降低系统性能,尽量少用

14、string字符串处理

构造函数:string(char[] chs)、string(char ch,int count)

  string str = new string(’a‘,’b’,’c’);//abc

  string str = new string(‘a’,3);//aaa

  3.144.ToString(“0.0”);用于控制格式,会四舍五入

字符串可以当做数组进行处理(使用下标读取,拥有数组的各种属性)

  string str = “不可见的你”;  此时str[1]就是“可”了

  通过索引得到的数据是char类型,字符串不可改变,使用索引无法修改字符串的数据

  要想修改可将字符串变成字符型数组,str.ToCharArray()

string是引用类型

string str = string.Empty;声明一个空字符串

  判断字符串是否为空:

str.Length == 0(推荐)

str ==“”

str == string.Empty

String.IsNullOrEmpty(要判断的字符串)(推荐)

15、string常用方法

  1)字符串比较:bool isTrue = string.Equals(string a,string b)

  String.Equals(str1,str2);

  Str1.Equals(str2,StringComparison.OrdinalIgnoreCase); //第二个参数意思是不区分大小写

  string.Compare(str1,str2);

  Equals方法:

String重载的方法判断两个,一个是==,一个是EqualsHelper

==判断两字符串是否相同

EqualsHelper判断两个字符串中每一个字符是否相同

  Object的重载是来自于object类,是继承下来的

在object中使用的是==和Equals方法

Equals方法是一个虚方法,由string重写了,调用的是object.ReferenceEquals方法和EqualsHelper方法

注意:==默认表示判断两个对象的地址是否相同,与object.ReferenceEquals方法一样

String提供的str1.Equals(str2)判断地址是否相同,字符串是否object提供的virtual Eaulse(object)

Equals和==的区别:

  Equals会判断两个值的内存地址;==只判断值是否相等。

Compare方法:

  int result = String.Compare(str1,str2);

  str1>str2 -> 1    str1 == str2 -> 0   str1<str2 -> -1

  string.Compare方法比较两个字符串,这两个字符串字母顺序表示大小,按照字典排序规则进行比较(对于char类型:A>a,B>a,b>A同一个字母,大写>小写,不同字母按照字母顺序后面的大于前面的;对于string类型不是按照埃克森码比较,A>a,b>A,B>a)

  2)大小写转换:ToLower()和ToUpper()

  3)str1 = str1.Trim([params char[] chs]);去除字符串两边的空格,或chs中出现的字符(重载)

     str1 = str1.TrimEnd(params char[] chs);去除字符串的末尾chs中出现的字符

     str1 = str1.TrimStart(params char[] chs);去除字符串的开头chs中出现的字符

  4)合并与分割

合并字符串:string s = string.Join(str1,str2,str3,...)

分割字符串:string[] strArray = str1.Split(‘a’);

                string[] strArray= str1.Split(new char[]{‘a’,’b’},StringSplitOptions.RemoveEmptyEntries);去掉a、b和所有空格

  5)字符串查找

Contain包含,返回bool类型的值,str1.Contain(“爱”);

IndexOf检索位置,返回字符串中某个字符的位置(int)找不到则返回-1

str.IndexOf(要找的字符或字符串)

str.IndexOf(要找的字符或字符串,开始寻找的位置)

str.LastIndexOf()是从后往前寻找,用法同str.IndexOf() 

注意:IndexOf和LastIndexOf中第二个参数(开始寻找的位置)只是为了说明要查找的是字符串中的哪一个字符(如果字符有重复),即按查找顺序在开始位置后的第一个字符,返回的索引依然是该字符在整个字符串中的索引,而不是从查找位置开始的索引。

  6)截取子字符串

string s =str1.Substring(开始的位置,子字符串的长度)

String s = str1.Substring(开始的位置)

  7)判断字符串的开头和结尾

bool a = str1.StratWith(字符串);

bool a = str1.EndWith(字符串);

  8)字符串的插入、移除和替换

插入:string str2 = str1.Insert(位置(int),要插入的人字符串(string));

移除:string str2 = str1.Remove(位置(int),长度(int));

      string str2 = str1.Remove(位置(int));移除该位置后的所有字符

替换:string str2 = str1.Replace(旧字符串,新字符串);

  9)格式化字符串

String.Format(格式化的字符串,填坑的参数)

string str1 = String.Format(“{0},{1}”,str2,str3);

  10)判断字符串是否为空

    String.IsNullOrEmpty(str);

  10)字符串的方法(总结)

增:添加(+=)、插入(Insert)

删:Remove

改:Replace、 ToUpper、ToLower、Trim、TrimStart、TrimEnd、Split、Join、new String、SubString、Format

查:Contains、IndexOf、LastIndexOf、StartsWith、EndsWith、ToCharArray、Length、Empty

比:Equals、Compare

16、StringBuilder

大量字符串拼接的时候性能非常差,很难完成大型数据的拼接

(Stopwatch对象可以记录程序运行的时间,Start开始计时,Stop结束)

使用StringBuilder:StringBuilder sb = new StringBuilder();

  sb.Append(要添加的字符串);//向sb中添加数据

  sb.AppendLine(要添加的字符串);//向sb中添加数据后换行,等价于sb.Append(要添加的字符串+”\r\n”);

  sb.AppendFormat(要添加的字符串);格式化处理,等价于 sb.Append(string.Format(要添加的字符串));

  string str = sb.ToString;输出或赋值时要转换成string

在处理大型数据的时候,StringBuilder要比普通的string处理快很多,因为处理大型数据要用StringBuilder

17、读取文件

  string[] lines = File.ReadAllLines(@"H:\传智播客实训资料代码\第9天\a.csv", Encoding.Default);//按行读取

  string[] lines = File.ReadAllText(@"H:\传智播客实训资料代码\第9天\a.csv", Encoding.Default);//读取所有文本

  string[] lines = File.ReadLines(@"H:\传智播客实训资料代码\第9天\a.csv", Encoding.Default);//读取一行文本

  string[] lines = File.ReadAllBytes(@"H:\传智播客实训资料代码\第9天\a.csv", Encoding.Default);//读取字节

18、文本高亮

->让文本框获得焦点--txtContent.Focus();

->找到要高亮的位置--pos

->选中(高亮)的字符串长度--length

->调用TextBox的选中方法txtContent.Select(pos,length)

19、泛型集合   List<T>和Dictionary<TKey,TValue>

List集合:动态的自定义数组

  List<类型> 集合名 = new List<类型>();

List用法与ArrayList一样,不同点在于定义时和使用时的类型固定,因此可以完全替代ArrayList

  类型[] = list.ToArray()//将List转换成该类型的数组

Dictionary集合:动态的自定义键值对

  Dictionary<类型,类型> 集合名 = new Dictionary<类型,类型>();

Dictionary用法与Hashtable一样,不同点在于定义时和使用时的类型固定,因此可以完全替代Hashtable

20、装箱与拆箱

  直接将值类型赋值给引用类型就是装箱

  将存储值类型的引用类型强制转化为对应的值类型就称为拆箱

Object o = 1;//装箱      int n = (int)o;//拆箱

  装箱对值类型保持无关性,装箱对引用类型保持相关性

  装箱和拆箱会对性能有一定的影响

21、读写数据(I/O操作、System.IO类)

  System.IO.Path类,专门处理字符串路径,是一个静态类

    string path = @“D:\window\system\file.txt”; 

    获取文件名:string fileName = Path.GetFileName(path);

    获取后缀名:string fileName = Path.GetExtension(path);

    修改后缀名:path = Path.ChangeExtension(待修改的路径,“mp3”);

    获取文件夹名:string fileName = Path.GetDirectoryName(path);

    合并路径:path= Path.Combine(路径1,路径2,路径3...);

    临时文件夹:path = Path.GetTempPath();

  绝对路径:从盘符开始的最精确的路径

  相对路径:相对于当前文件同一个目录作为参照的路径

  相对路径和绝对路径:区分就看有没有根目录盘符的标记

22、文件流(FileStream)

  1)引入命名空间

  2)创建FileStream对象FIleStream fs = new FileStream(文件名,FileModel,FileAccess)

FileStream fs = new FileStream(@"D:\a.txt", FileMode.Create, FileAccess.Write);

FileModel:对文件的处理方式(打开、创建、追加)

FileAccess:对文件的操作:读、写

  3)使用方法操作

写一个字节:fs.WriteByte()

写一个字节片段(将字节数组中的数据写入到fs指定的文件):

  fs.Write(存放字节的数组,开始写字节的数组下标,要写的字节数)

读一个字节:fs.ReadByte()

读一个字节片段(将fs指定的文件中的数据读取到字节数组):

  fs.Read(存放字节的数组,开始存放字节的数组下标,要读的字节数)

  4)释放资源

fs.Close()和fs.Dispose()方法(两个都要调)

23、StreamReader和StreamWriter

  StreamReader:专门用来读取文本文件的流

1)引入命名空间:IO

2)new一个FileStream指向文件

3)new一个StreamReader,构造函数参数是FileStream的对象

4)调用方法

    StreamReader有一个属性EndOfStream:判断当前的流位置是否在文件流的末端,一般用来判断文件是否读完

  StreamWriter:专门用来写入文本文件的流

1)引入命名空间:IO

2)new一个FileStream指向文件

3)new一个StreamWriter,构造函数参数是FileStream的对象

4)调用方法

24、File和Directory对文件和目录操作做了一个封装

增(Create):创建文件或文件夹  

删(Delete):删除文件或文件夹  

改:改名字 File.Move(“1.txt”,“2.txt”)

查:...

判断是否存在(Exists)

移动文件:File.Move(“1.txt”,“2\1.txt”)

复制文件:Copy

  Directory用于处理文件夹(目录)

删除目录的时候如果目录不是空的则会报错,此时需要在Delete内添加一个true

Directory.Delete(“1”,true);

25、序列化:

  序列化:把一个对象的内容存储到磁盘上(文件流)、网络上(网络流)、内存里(内存流),下次需要用到对象数据的时候可以直接拿来用

  反序列化:把已经序列化存储到磁盘、网络、内存里的对象数据,提取到项目中的对象中

序列化步骤:

1)创建一个文件流

2)为需要序列化的对象添加标记([Serializable]表示此对象可以被序列化)

3)创建BinaryFormatter(导入命名空间)

4)调用Seralize方法(bf.Seralize(文件流对象,需要被序列化的对象))

反序列化步骤:

1)创建一个文件流

2)为需要序列化的对象添加标记([Serializable]表示此对象可以被序列化)

3)创建BinaryFormatter(导入命名空间)

4)调用DeSeralize方法(object ob = bf.DeSeralize(文件流对象))

反序列化定义接收类型的时候必须根据原序列化的程序集确定类型

  建议:在使用序列化的时候尽量避免使用自动属性,因为自动属性在每次编译的时候自动生成的字段名可能不一样,所以在反序列化的时候可能会造成问题

  注意:如果序列化和反序列化不在同一项目下,那反序列化的时候需要引用原序列化的程序集

26、关于文件夹下的文件与子文件夹

  Directory类的静态方法

获取子文件名:string[] s = Directory.GetFiles(@“D:\dir“);

获取特定后缀名的子文件名:string[] s = Directory.GetFiles(@“D:\dir“,”*.txt“);

获取所有文件夹名:string[] s = Directory.GetDirectories(@“D:\dir“);

27、正则表达式

  元字符:

1).(点)表示除\n以外任意的单个字符

2)[ ]表示括号内的任意一个字符(如a[xyz]b表示axb或ayb或azb)

3)^在[ ]括号内表示取反(如^[a-z]表示除小写字母以外的所有字符)

4)^匹配一串字符的开始(^abc表示以abc为开头的任意字符串)

5)$匹配一串字符的结尾(abc$表示以abc为结尾的任意字符串)

5)|表示“或”,优先级最低(如a|bc表示a或bc,(a|b)c表示ac或bc)

6)()可以改变优先级、提取组

7)*表示限定前面的字符出现任意次数(abc*表示ab、abc、abcccc......(abc)*表示abc、abcabc)

8)+ 至少出现一次,也可出现多次(xa+y表示xay、xaay...)

9)?表示出现0次或1次

10){n}限定前面的表达式出现n次

11){n,m}至少出现n次,最多出现m次

12){n,}至少出现n次

13)\d表示0-9,\D表示\d的反面

14)\s表示空白符,\S表示\s的反面

15)\w表示数字、字母、下划线、汉字,\W表示\w的反面

注:正则表达式的转义符也是\,如果要表示*(星),可以写成\*,要表示一个\,可以写成\\

28、Regex类

  Regex.IsMatch(待判断的字符串,正则表达式)判断一个字符串是否匹配某个正则表达式

  !如果正则表达式没有^和$,那默认不设定开头和结束,此时只要待判断的字符串中含有该正则表达式就返回true

  ^z|food$:表示以z开头的任意字符串或者以food结尾的任意字符串,都返回true

             因为|的优先级最低,所以上边的表达式其实是(^z)|(food$)

  Regex.Match()从某个字符串中提取匹配某个正则表达式的某个子字符串,但只能提取一个

  Regex.Matches()同上,可以提取所有的匹配字符串

  Regex.Replace()字符串替换,把所有匹配正则表达式的字符串替换为对应的字符

29、字符串提取

  字符串提取的时候一般不加^和$  

  Match m = Regex.Match(原字符串,要提取的正则表达式)

  m.Value属性表示提取到的字符串

  m.Group[n]:提取组,按正则表达式中()括号对的个数,m.Group[0]表示整个字符串,m.Group[1]表示第一个括号内的内容,所以提取组的时候要从下标1的地方开始

  MatchCollection mc = Regex.Matches(原字符串,要提取的正则表达式)

  MatchCollection是一个集合,需要foreach遍历输出(遍历时的类型是Match)

30、贪婪模式

  定义:当正则表达式提取的时候,如果一个字符或多个字符都能匹配,这时会按照使用最多字符的方式来匹配。

  正则表达式默认采用贪婪模式,尽多的匹配

  在限定符后面使用问号用来终止贪婪模式,即只会提取匹配的第一个

  string s = “aaaa bbbbbbbb”;string s1 = “[a-zA-Z]+”;  匹配结果:aaaa

  string s = “aaaa bbbbbbbb”;string s1 = “[a-zA-Z]+?”;  匹配结果:a

31、字符串替换

  String s =Regex.Replace(原字符串,待替换的正则表达式,替换后的字符串);

  如:string s = Regex.Replace(s, "-+", "-");//把字符串s中的所有-替换成一个-

  提取组替换:

            string s = "aaa13812345678bbb";

            s = Regex.Replace(s, @"(\d{3})(\d{4})(\d{4})", "$1****$3");//把手机号中间4位替换成4个*

32、byte数组和字符串的转换

  byte[] bt= System.Text.Encoding.UTF8.GetBytes(str);

  string str = System.Text.Encoding.UTF8.GetString(bt);

33、委托(delegate)

  委托是一种数据类型(和类一样),可以将方法赋值给委托对象,可以理解为方法类型

  定义(在命名空间下,或添加一个cs文件,和类的定义一样):

1)使用delegate关键字

2)这个委托将来要存储的方法如果没有返回值,那么委托也要定义成void,参数也要与方法保持一致

3)委托是一个数据类型,用的时候需要传递一个变量

4)创建委托对象的时候可以不用new,系统编译时会自动给我们new

定义委托类型:public delegate void ChangeTxtDelegate(string str);

创建委托对象:public ChangeTxtDelegate changeTxt;  //Form1中

    给委托赋值:f1.changeTxt = ChangeText;  //Form2中

调用委托:changeTxt(txt);  //Form1中,此种调用方法为简写,实际上是changeTxt.invoke(txt);

创建委托对象时也可直接对其赋值:ChangeTxtDelegate changeTxt = new ChangeTxtDelegate(ChangeText) ;

  用法(窗口间传值):委托要声明在需要被传值的对象内,比如Form1要提取Form2中的数据(即Form2往Form1传值),就应该把委托声明在Form1中,在Form2中写一个方法获取要传的数据,并且赋值给Form1的委托,最后在Form1中调用委托就ok了。(委托声明位置不绝对,还需要考虑当前活动窗口的问题,要声明再活动窗口中,有时也可声明在被提取值的窗口内)

  作用:可以在某个代码内部,嵌入一段外部代码(外部写一个方法赋值给委托或在定义方法时把委托类型作为参数,调用时把方法名作为参数传递给委托对象,那么委托就执行该方法,不同的外部项目可以写不同的方法赋值给委托,可以实现一个委托选择性执行多种方法的灵活性),相当于是注入

  什么情况下使用委托?

    当一个类型中需要嵌入一段代码,但是这段代码具有不确定性,是根据使用这个类型的用户来确定代码的,这时就可以在该类型中使用一个委托,保证在某种情况下会调用该委托,用户将对应的方法传递给该委托,就会调用这个方法

  可以把静态方法或者私有方法赋值给委托了变量,赋值后只要能使用到该委托变量的地方就能使用该方法,打破了访问修饰符的限制

  一般在调用委托前或者触发事件前,都要判断委托变量或事件是否为null,如果为null则不调用

34、多播委托

  MyDelegate md = new MyDelegate(M1);  md+=M2;(增加某个方法)md-=M4;(去掉某个方法)

  使用委托时,如果不是+=而是直接用=赋值,则会将前面增加的所有方法都覆盖掉

  多播委托可以存储多个方法,委托中方法调用的顺序与增加方法时的顺序是一致的,但是,不要依赖于这个顺序(微软没有承诺这样的顺序,万一以后改了就GG了)

  多播委托中,如果要是有返回值,只会得到最后一个方法返回的结果。可以通过调用GetInvocationList()方法返回当前委托中的所有方法,返回值类型时一个Delegate数组(委托数组),再用foreach遍历调用所有方法

  所有定义的委托都继承自抽象类MulticastDelegate,而MulticastDelegate又继承自Delegate抽象类

  多播委托内部是将绑定在当前委托对象上的每个方法,都转换成一个委托对象,并且存储在了一个叫_invocationList的object数组中。当调用委托的时候,其实就是循环遍历_invocationList数组,并且调用其中的每个委托

  多播委托中,如果其中的某个方法执行时发生了异常,则后面的方法不再执行。

  多播委托中只能存储同一类型的委托,即返回值、参数都相同

35、事件

  事件是在特定条件下执行的动作,这个动作是由用户确定的。如Button的Click事件,其动作是由程序员写的,因此可以用委托来实现这个功能,写不同的方法赋值给委托变量。

  但用委托模拟方法有两个缺点:

  1)定义的委托可以在类外部的任何地方被触发,即非特定条件下也可以调用。因为委托变量是public修饰,改为private就不能在外部赋值了。

  2)由于委托可以用=(等号)赋值,所以就有可能把前面已经注册的事件处理程序都覆盖掉。

  而用事件就可以解决以上问题:

  1)用事件必须先定义一个委托,否则无法使用事件

  2)实例化委托变量的时候,在委托类型名前面、访问修饰付的后面加event关键字,就定义了一个事件

  3)使用方法与委托一样,只是避免了以上两个问题

  4)由于事件只能有+=或-=赋值,所以就避免了用=(等号)赋值时覆盖前面事件处理程序的问题

  5)由于事件不能在定义事件的类以外被触发,所以也就避免了冒充事件触发的问题。

36、委托和事件的区别(面试常考)

  委托和事件没有可比性,委托是数据类型,事件是委托类型的变量

  委托可以用+=、-=、=赋值,可以在类的外部被调用

  事件只能用+=、-=赋值,不能在类的外部被调用

  事件的内部是用委托实现的

  事件的作用和委托变量(不是委托,是委托变量)一样,只是在功能上受到一定的限制

37、var类型

  var只能用作局部变量,可以赋任何类型的值。

  var a = 1;和int a = 1;两者完全一样

  var不能作为类的成员的类型,不能用作参数,不能用作返回值,只能用作局部变量

38、扩展方法(不修改微软系统方法源代码,实现系统方法的增加)

  1)添加一个静态类

  2)在静态类中增加一个静态方法,方法参数是:(this+要扩展的类名+变量名,该方法的参数)

  如要在string类中添加一个验证是否是Email格式的扩展方法:

        public static bool IsEmail(this string str)

        {

            return Regex.IsMatch(str, @"^\w+@\w+\.\w+$");

        }

            string a = "1321321@qq.com";

            Console.WriteLine(a.IsEmail());

  扩展方法只是看起来像类中的方法,其实不是,所以在扩展方法中也访问不到类中原来的私有成员

  因为会扰乱系统方法,所以不建议使用,尽量不用

39、XML

  XML是用一种格式化的方式来存储数据,可以用记事本打开

  1)XML有且只有一个根元素

  2)有开始标签就必须得有结束标签,且区分大小写

  3)元素的属性值要用引号

  读取XML文件,通过XDocument(需要添加命名空间)

  读取整个XML文件: XDocument xd = XDocument.Load(XML文件路径)

  循环遍历每个节点:

1)获取根节点  XElement xeRoot = xd.Root;(xeRoot.ToString()表示XML文档的内容)

2)递归遍历XML中的每个节点

  遍历xeRoot下面的所有子节点:

     public static void GetEle(XElement xe)

            {

                foreach (XElement ele in xe.Elements())

                {

                    Console.WriteLine(ele.Name);

                    GetEle(ele);

                }

            }

           GetEle(xeRoot);

例:读取XML数据

<CFX>

  <MSG>

     <交易码val="1000" />

     <流水号val="100000000000000001" />

     <金额val="1234567890.12" />

     <付款机构val="1234" />

     <付款单位账号val="12345678901234567890" />

     <收款机构val="1234" />

     <收款单位账号val="12345678901234567890" />

  </MSG>

  <MSG>

     <交易码val="1000" />

     <流水号val="100000000000000002" />

     <金额val="1234567890.12" />

     <付款机构val="1234" />

     <付款单位账号val="12345678901234567890" />

     <收款机构val="1234" />

     <收款单位账号val="12345678901234567890" />

  </MSG>

</CFX>

    VS中读取XML的数据:

            XDocument xd = XDocument.Load("ytbank.xml");  //加载XML文件

            XElement xeRoot = xd.Root;  //获取根节点

            foreach (XElement item in xeRoot.Elements())  //遍历根节点,获取第一级子节点(MSG)

                {

                  foreach (XElement itema in item.Elements())  //遍历第一级子节点,获取第二级子节点

                    {

                    Console.WriteLine("{0}:{1}", item.Element(itema.Name).Name, item.Element(itema.Name).Attribute("val").Value);  //获取节点名和属性值

                    }

                Console.WriteLine("==================================");

              }

XML写入:

            XDocument xdoc = new XDocument();  //创建一个XML对象

            XElement xeleRoot = new XElement("Websites");  //创建一个根节点

            xdoc.Add(xeleRoot);  //将根节点添加到XML文件中

            xeleRoot.SetElementValue("baidu", "http://www.baidu.com");  //添加一个节点以及内容       

            XElement xeleGoogle = new XElement("website");  //创建一个节点

            xeleGoogle.SetAttributeValue("url", "http://www.google.com");  //添加属性和属性值

            xeleGoogle.SetElementValue("name", "谷歌"); //为节点添加子节点

            xeleGoogle.SetElementValue("age", "14");

            xeleGoogle.SetElementValue("boss", "谢盖尔");

            xeleRoot.Add(xeleGoogle); //将节点添加进根节点

            xdoc.Save("website.xml");  //保存一个XML文档路径