当前位置 博文首页 > 流浪若相惜的专栏:唤起你对c#曾经的记忆

    流浪若相惜的专栏:唤起你对c#曾经的记忆

    作者:[db:作者] 时间:2021-08-01 08:40

    在我们很久不进行编程开发时,往往会有种抽空的感觉,好多东西一看就懂,但是在不看时会有种茫然无意识的感觉,为了快速的恢复已有的记忆,本文从自己编程学习的经验入手,整理了我们长时间不进行程序开发,容易忘记的知识点。

    									***不断更新中...**
    

    注释

    (不写注释是流氓,名字瞎起是扯淡)//借赵老师的一句话
    ‘///’一般用于注释函数,注释类。

    快捷键

    ctrl+k+d(有语法错误无法进行对齐)
    ctrl+j(快速弹出只能提示)
    shift+end,shift+home 键从行首到行尾,从行尾到行首;
    ctrl+k+c 注释  ctrl+k+u取消注释
    ctrl+R+e 快速生成属性get和set方法
    alt+shift+f10 打开说明引用空间。
    f1转到msdn
    f12转到定义
    

    数据类型

    decimal money=5000m;(金钱类型)
    String 和string在c#中都一样。String是.net平台中共用类型,string是c#中专有的,它们两个都会映射到System.class类中。
    

    ##数据类型转换
    隐式类型和显式类型转换的基本要求:两种类型可以兼容。
    1、隐式类型转换(自动类型转换)
    小转大
    例如:

    	   int b=10;
    	   double a=10
    	   Console.WriteLine(a);
    

    2、显式类型转换(强制类型转换)
    大转小
    例如:

       double b=10.4
       int a=(int)b;
       Console.WriteLine(a); 
    

    整数的+、-、*、/、%仍然是整数
    例如:
    int a=10;
    int b=3;
    double c=a/b
    得出的结果仍然是整数:3
    如果想获取3.33333…,表达式中必须得有double类型的数。

    命名规范

    *1、Camel命名规范(多用于变量命名):首单词首字母小写,其余单词首字母大写。
    *2、Pascal命名规范(多用于类或者方法命名):所有单词首字母大写。
    

    占位符

    使用方法:先挖个坑,再填个坑。
    Console.WriteLine("..{0},...{1},...{2}",n1,n2,n3);
    

    ##转义符 ##
    \n:换行,但是在windows系统中不认识\n,只认识\r\n;
    ":输出半角引号
    \t:tab键(排版)
    \b:退格,首尾放置无效,只是退格一个。
    @:1、取消\在字符串中的转义作用。2、保留原格式输出。
    ##枚举##
    ===枚举与int和string之间转换=
    @1、枚举类型默认可以和int类型相互转换 枚举类型跟int是兼容的
    @2、枚举类型转换成string,用toString();
    @3、字符串string类型转换成枚举类型:
    枚举类型 对象名 =(枚举类型)Enum.Parse(typeof(枚举名),string s);
    ##Convert.ToInt32、Int32.Parse、Int32.TryParse区别
    引用:Difference Between Int32.Parse(), Convert.ToInt32(), and Int32.TryParse()
    1)、Int32.parse(string)
    Int32.Parse (string s) method converts the string representation of a number to its 32-bit signed integer equivalent. When s is a null reference, it will throw ArgumentNullException. If s is other than integer value, it will throw FormatException. When s represents a number less than MinValue or greater than MaxValue, it will throw OverflowException. For example:

    string s1 = "1234"; 
    string s2 = "1234.65"; 
    string s3 = null; 
    string s4 = "123456789123456789123456789123456789123456789"; 
    
    int result; 
    bool success; 
    
    result = Int32.Parse(s1); //-- 1234 
    result = Int32.Parse(s2); //-- FormatException 
    result = Int32.Parse(s3); //-- ArgumentNullException 
    result = Int32.Parse(s4); //-- OverflowException 
    

    2)、Convert.ToInt32(string)

    Convert.ToInt32(string s) method converts the specified string representation of 32-bit signed integer equivalent. This calls in turn Int32.Parse () method. When s is a null reference, it will return 0 rather than throw ArgumentNullException. If s is other than integer value, it will throw FormatException. When s represents a number less than MinValue or greater than MaxValue, it will throw OverflowException. For example:

    result = Convert.ToInt32(s1); //-- 1234 
    result = Convert.ToInt32(s2); //-- FormatException 
    result = Convert.ToInt32(s3); //-- 0 
    result = Convert.ToInt32(s4); //-- OverflowException 
    

    3)、Int32.TryParse(string, out int)

    Int32.Parse(string, out int) method converts the specified string representation of 32-bit signed integer equivalent to out variable, and returns true if it is parsed successfully, false otherwise. This method is available in C# 2.0. When s is a null reference, it will return 0 rather than throw ArgumentNullException. If s is other than an integer value, the out variable will have 0 rather than FormatException. When s represents a number less than MinValue or greater than MaxValue, the out variable will have 0 rather than OverflowException. For example:

    success = Int32.TryParse(s1, out result); //-- success => true; result => 1234
    success = Int32.TryParse(s2, out result); //-- success => false; result => 0
    success = Int32.TryParse(s3, out result); //-- success => false; result => 0
    success = Int32.TryParse(s4, out result); //-- success => false; result => 0
    

    Convert.ToInt32 is better than Int32.Parse since it returns 0 rather than an exception. But again, according to the requirement, this can be used. TryParse will be the best since it always handles exceptions by itself.
    ##字段与变量区别##
    1、字段与变量的最根本区别在于字段可以存储多个值,而变量只能存储一个值。
    2、字段和变量的写法区别是字段前边加一个_
    例如:

    public struct Person
    {
    	public int  _code;
    	public string  _name;
    	public  string  _gender;
    }
    //声明两个结构体
    Person person1;
    person1._name;
    Person person2;
    person2._name;
    //字段可以多个值。person1的名字跟person2的名字。
    

    ##属性##
    惯用法:属性开头字母大写,字段开头字母小写。

    class  Person
    {
    	private  int age;
    	
    	public int Age
    	{
    			 set
    		{
    		  this.age=value;
    		}
    		 get
    		{
    		   return this.age;
    		}
    
    }
    

    ##c#中无全局变量一说,只能用静态字段来模拟全局变量。##
    ##c#中三大参数##
    1)out参数:
    如果在一个方法中返回多个相同类型的值的时候,可以考虑返回一个数组。
    但是返回多个不同类型的值时就需用out。out既可以返回多个相同值也可以返回多个不同值。
    其实out作用就是:返回被调用函数的多个值。与一般返回方向是相反的。
    注意:out定义变量必须在被调用函数中赋值。
    2)ref参数:
    能够将一个变量带入一个方法中改变,改变完成后,再将改变后的值带出方法。

    main()
    {	
    	int s=10;
    	Test(ref s);
    
    }
    Test(ref s1)
    {
    	s1+=3
    }
    

    3)parame参数:
    可变参数
    将实参列表中与可变参数数组中类型一致的类型当作数组的元素。既实参可以不用声明数组。
    main
    {
    test(“为”,34,389,34);
    }
    test(string name,params int[] score)
    {
    }
    ##方法的重载和返回值无关,至于方法名、参数类型、个数有关。##
    ##静态与非静态的区别##
    1)、在非静态类中,既可以有实例成员,也可以静态方法。静态类中只能出现静态成员。
    2)、在调用实例成员时候,需要使用对象名.实例成员;
    在调用静态成员的时候,需要类名.静态方法。
    总结:静态成员必须使用类名去调用,实例成员用对象去调用。
    静态函数中只能访问静态成员,不允许访问实例成员。
    实例方法既可以使用静态成员,也可以使用实例成员。
    静态类中只能出现静态成员。静态类无法实例化。
    使用:
    1)、如果你想要你的类当做一个“工具类”去使用,这个时候可以考虑写成静态的类
    2)、静态类在整个项目资源共享。静态类是占内存的。
    GC Garbage Collection 垃圾回收器。
    ##构造函数##
    作用:帮助我们初始化对象,构造函数是一个特殊的方法。
    1)构造函数没有返回值,连void也没有
    2)构造函数的名称必须和类名一样。
    3)访问修饰符必须是public
    4)创建对象的时候执行构造函数
    ##new##
    1)在内存中开辟一块空间;
    2)再开辟的空间中创建对象;
    3)调用对象的构造函数初始化对象。
    构造函数必须是public。
    ##this##
    1)代表当前类的对象。
    2)在类当中显式调用本类的构造函数 :this
    如有多个构造函数,构建一个全属性的构造函数,其它构造函数调用这个全的构造函数,减少代码冗余。
    写法:
    在不全的构造函数后面加:this

    	public Person(string name,int age,int english,int math,int chinese)
    	{
    		// 赋值给属性
    		this.Name=name;
    		this.Age=age;
    		this.English=english;
    		this.Math=math;
    		this.Chinese=chinese;
    	}
        public Person(string name,int english):this(name,0,english,0,0)
        //没有的可以随便给该变量赋值该类型的数
        {
    	    
        }
    

    ##在一个项目中引用另一个项目的类##
    1)、先引用项目
    2)、引用另一个项目的命名空间。
    ##值类型和引用类型##
    区别:
    1、值类型和引用类型在内存上存储的地方不一样
    2、在传递值类型和传递引用类型的时候传递的方式不一样。
    值类型我们称之值传递,引用类型我们称之为引用传递。

    值类型:int 、double、char、bool、decimal、struct、enum
    引用类型:string、自定义类
    存储:
    值类型存储在内存的栈中。
    引用类型存储在内存的堆中。
    

    ##字符串##
    1)、字符串的不可变性,如果字符串被重新赋值,老值并不删除,而是在栈中将老的
    地址删除,重新给字符串赋予新的地址值。
    当程序结束后,GC扫描整个内存,如果发现有的空间没有指向,则立即销毁。
    2)、可以将string类型看作是char类型的只读数组。
    例:
    string s=“abcde”;
    //S[0]=‘B’;不能这么做,因为是只读的
    //如果改变的话,首先需进行对string的转换。
    char []chs=s.ToCharArray();
    //将字符数组转换成我们的字符串
    s=new string(chs);
    Console.WriteLine(s[0]);
    //字符串转字节数组:
    byte buffer=Encoding.Defalut.GetBytes(str);
    //字节数组转换成字符串:
    string str=Encoding.Defalut.GetString(buffer);
    ##StringBuilder##
    当字符串进行大量的循环累加时,会进行大量的开辟新的内存空间,比较慢。
    计时:Stopwatch Start(); Stop(); Stopwatch.Elapsed //记录运行的总时间。
    StringBuilder.Append(i);追加方式进行累加。
    StringBuilder会节省很大时间,原因在于它不开辟新空间,然后再将StringBuilder转换为String
    ##字符串方法##
    1)、Length()
    2)、ToUpper()
    3)、ToLower()
    4)Equals(str1,StringComparision.OrdinalIgnoreCase):比较两个字符串是否一致
    ,并忽略大小写。
    5)、Splite(字符数组,StringSplitOptions.RemoveEmptyEntries),则将字符数组内容删除,并且不返回空数组和null。
    6)、Contains()
    7)、Replace(str1,str2)
    8)、string.Substring();
    9)、string.StartWith()/EndWith()
    10)、string.indexof()返回int类型,返回字符串首先出现的位置
    11)、string.LastIndexof()最后一个字符串出现的位置。
    12)、string.trim()移除字符串中所有空格。string.trimStart()/trimEnd();
    13)、string.IsNullOrEmpty()
    14)、string.Join();能够将指定的分隔符加入到数组中进行分割。
    ##类的继承##
    子类不继承父类中的构造函数,但是,子类会默认调用父类无参的构造函数
    创建父类对象,让子类可以使用父类中的成员。
    所以,如果在父类中重新写了一个有参数的构造函数之后,无参数的构造函数就被干掉了。
    子类就调用不到了,所以子类就会报错。
    解决方法:
    1、在父类中写一个无参数的构造函数。
    2、在子类中显示的调用的父类的构造函数,使用关键字:base(参数)(基类的有参构造函数)。
    ##new关键字##
    父类中有个Say()方法,子类中也有个Say()方法,理论上会隐藏,但是会有错误,
    解决方法:
    在子类中Say()方法前加new,隐藏后的结果就是钓不到父类的成员。
    ##里氏转换##
    1)、子类可以赋值给父类,如果有一个地方需要父类作为参数,可以用一个子类代替。

    Student s=new Student();
    	Person p=s;
    	或者
    	Person p=new Student();
    
    这样做的可以实现string.join("|",new string[]{"1","2","3","4"})
    第二个参数是 parame object 类型,object是一切类的基类,string类型是object类的继承类。这样写可以实现任何继承于object类的继承类均可以实现join第二个参数数组形式。
    2)、如果父类中装的是子类对象,那么可以将这个父类强制转换为子类对象。
    
    Person p=new Student();
    Student stu=(Student)p;//这样才能调用子类成员。
    

    ##is/as(类型转换)##
    is:如果能够转换成功,则返回一个true,否则返回false
    as:如果能够转换则返回一个对象,否则返回一个null

    	Person p=new Student();
    	Student stu=p as Student;
    

    ##ArrayList
    集合:1、长度可以任意改变,类型随便。
    ArrayList.Add(Object);
    ArrayList.ArrayRange(数组/集合);
    注:我们将一个对象输出到控制台,默认情况下 打印的就是这个对象所在的类的命名空间。
    List.clear()清空所有元素。
    List.Remove();删除单个元素;
    List.RemoveAt(索引/下标)
    List.RemoveRange(下标,长度)//根据索引删除一定范围内的元素。
    List.Reverse()//将所有元素反转
    List.Insert(索引,object)//在某个位置插入单个元素
    List.InsertRange(索引,数组)//在指定的位置插入一个集合。
    List.Contains()//判断包含内容
    集合的长度问题
    count/capcity
    count:表示集合实际包含的元素个数.
    capcity:表示集合可包含的元素个数。
    实际包含的个数每次超过4就会重新申请内存空间,增加的个数为4的倍数。
    ##HashTable键值对集合
    利用foreach循环来遍历键值对,格式:

    foreach(var item in collection)
    	{
    	}
    
    var:根据值可以推断类型
    foreach(var item in HashTable.Keys)
    

    ##List泛型
    List list=new List();//和ArrayList的区别在于它确定类型
    List泛型集合转换为数组:
    int[] nums=list.ToArray();//集合是int类型
    ##Dictionary
    Dictionary <,>dic=new Dictionary<,>;
    遍历可以使用

    foreach(var item in dic.Keys)
    {
    }
    或者
    foreach(KeyValuePair<,>kv in dic)
    {
    	kv.Key..
    	kv.Value..
    } 
    

    ##FileStream
    FileStream是用来操作字节的(任意文件)
    StreamReader/StreamWriter用来操作字符的(文本文件)大文件

    FileStream fsRead=new FileStream(path,FileMode,FileAccess);
    byte[] buffer=new byte[1024*1024*5]
    //返回实际文件长度
    int length=fsRead.Read(buffer,0,buffer.Length);
    //将自己数组中每一个元素按照编码格式解码成字符串
    string str=Encoding.Default.GetString(buffer);
              =Encoding.Defalut.GetString(buffer,0,length);//这段代码会解码文件长度的字符串。
    //关闭流
    fsRead.Close();
    //释放流所占的资源
    fsRead.Dispose();
    

    将创建文件流对象的过程写在using中,会帮助我们自动释放文件流所占用的资源。
    using(FileStream fsWrite=new FileStream(path,FileMode,FileAccess))
    {
    byte[] buffer=new byte[length];
    fsWrite.Write(buffer,0,buffer.Length);
    }
    注意:写文件和读文件都需要统一编码格式。
    多媒体文件复制:

    
    string source=pathSource;
    string target=pathTarget;
    创建一个读取文件的流
    using(FileStream fsRead=new FileStream(pathSource,FileMode,FileAccess))
    {
    	//创建一个写入的流
    		using(FileStream fsWrite=new FileStream(pathTarget,FileMode,FileAccess))
    		{
    			 byte[] buffer=new byte[1024*1024*5]
    			 //因为文件可能会比较大,所以我们在读取的时候应通过一个循环去读取
    			 while(true)
    			 {
    				//返回本次读取实际读到的字节数
    				int length=fsRead.Read(buffer,0,buffer.Length);
    				//如果返回0,也就意味什么都没有读到,读取完了
    				if(length==0)
    				{
    					break;
    				}
    				fsWrite.Write(buffer,0,length);			
    		    }
    		}
    }
    

    StreamReader和StreamWriter

    using(StreamReader sr=new StreamReader(path,Encoding.Default))
    {
          while(!sr.EndOfStream)
    	{
    		sr.ReadLine();
    	}
    }
    
    using(StreamWriter sw=new StreamWriter(path))
    {
    	string str="......";
    	sr.Write(str);
    }
    

    ##多态
    多态:让一个对象表现出多种类型
    三种:虚方法、抽象类、接口
    原因:子类赋值给父类只能调用父类的方法
    1)、虚方法
    将父类的方法标记为虚方法,使用关键字virtual,这个函数可以被子类重新写一遍。
    在子类前边加override
    2)、抽象类
    当父类中的方法不知道如何去实现的时候,可以考虑将父类写成抽象类,将方法写成抽象方法。(函数没有实际的价值)
    标记一个类为抽象类加abstract,加入abstract的方法就是抽象方法,抽象方法没有方法体
    public abstract void 函数名();
    子类中重写抽象方法

    public override void 函数名()
    {
    }
    

    调用:
    Animal a=new Dog();//抽象类无法实例
    a.Bark();//方法是父类的函数,但是子类重写了,所以调用子类的函数。
    抽象类与虚方法的区别在于:父类中方法有无实现。
    3)、接口(属性也是两个方法,get、set,因此接口中只能有方法)
    interface名字命名一般是I…albe。
    #1、接口中成员不允许添加修饰符,默认就是public
    #2、不允许写具有方法体的函数
    #3、接口中不允许写字段
    #4、接口可以继承接口
    #5、如果接口即继承了类,又继承了接口,应该先写类。
    #6、显式实现接口

      public interface IFlyable
      {
    	  void fly();
      }
      public class Bird:IFlyable
      {
    	  public void fly()
    	  {
    	  }
    	  void IFlyable.fly()
    	  {
    	  }
      }
    

    ##访问修饰符
    public
    protected
    private
    internal:只能在当前程序集中访问。(当前项目内部)
    能够修饰类的修饰符有两个:public 和internal
    protected internal
    ##值传递和引用传递
    值类型在复制的时候,传递的是这个值的本身。
    引用类型在复制的时候,传递的是对这个对象的引用。
    Person p1=new Person();
    p1.Name=“张三”;
    Person p2=p1;
    p2.Name=“李四”;
    Console.WriteLine(p1.Name);
    输出应该是李四,原因是p1、p2在堆中使用同一个引用。

    				|:-------栈---------:|:-------------堆----:|
    Person p1		|    	p1		     |                    |
    
    Person p2                            |	new Person();     |
    p2=p1;		    |			p2	     |                    |
    

    ##序列化与反序列化
    序列化就是将对象转换成二进制
    反序列化是将二进制转换成对象。
    作用:传输数据。
    序列化:
    1)、在序列化类前边加上[Serializable]
    2 )、 开始序列化对象
    BinaryFormatter bf=new BinaryFormatter();
    bf.Serialize(fsWrite,对象类)//fsWrite创建一个FileStream类对象写对象,自动调用Write()方法。
    反序列化Deserialize()
    ##部分类
    在一个项目中如果使用两个同名类,目的是共同开发书写,在前边加一个partial
    ##密封类
    在类前边加一个sealed
    特点:密封类是不能够被继承的。
    ##MD5加密

    //创建一个MD5对象,MD5为抽象类,无法实例化。调用Create方法创建一个对象
    MD5 md5=MD5.Create();
    //开始加密
    byte []buffer=Encoding.Default.GetBytes(str);
    //加密需要调用ComputeHash方法,方法中的参数是byte[]类型,并返回一个byte[]类型
    byte []md5Buffer=md5.ComputeHash(buffer);
    //将字节数组转换成字符串
    
    //将字节数组中的每一个元素ToString();原因是会出现乱码
    //return Encoding.Default.GetString(md5Buffer);
    
    for()
    {
    	strNew+=md5Buffer[i].ToString("x2");//转换成16进制,加2对其
    }
    

    ##进程(Process)
    获取进程:Process.GetProcesses();
    杀死进程:Process.Kill();
    打开进程:Process.Start();
    通过一个进程打开一个文件

    ProcessStartInfo psi=new ProcessStartInfo(filename);
    Process p=new Process();
    p.StartInfo=psi;
    p.Start();
    

    ##线程
    单线程的问题:让程序智能做单一的一件事,容易出现假死的状态。
    创建线程:

    Thread th=new  Thread(方法名);
    th.IsBackgroud=true;//创建一个后台线程。
    th.Start();//标记着这个线程准备就绪了,可以随时被执行。
    

    注:在.Net下是不允许跨线程的
    取消跨线程访问:
    在winform的form_load下:

    Control.CheckForIllegalCrossThreadCalls=false;//取消对线程跨线程访问检查的设置
    

    这样会造成异常,原因在于:该线程在使用主线程(窗体)的一些资源,关闭主线程(窗体)可能创建的线程还没有完成,它调用者主线程的资源但是主线程已经关闭了,于是就抛异常了。
    解决方法:
    在winform窗体的属性中点击FormClosing事件:
    if(新线程!=null)
    {
    新线程.Abort();//关闭线程
    }
    具体什么时候执行这个线程由CPU决定
    例如:窗体上输出从1~10000,我们可以将这个方法作为一个线程,主线程就可以对窗体进行其他操作,如移动、关闭等操作。否则的话会出现假死状态。
    前台线程:只有所有的前台线程都关闭了,程序才能关闭
    后台线程:只有所有的前台线程结束,后台线程自动结束,
    Thread.Sleep(3000);

    ##委托:
    1、委托是指具有相同属性(也称具有相同的函数签名:返回类型相同,参数类型、参数顺序及参数个数相同)的函数或方法的抽象,关键字为delegate。主要用途是三个:1)函数回调;2)传递方法;3)事件机制
    2、匿名函数:
    没有名字的函数
    public delegate void DelSayHi(string name):
    void mian(string [] args)
    {
    DelSayHi del=delegate(string name){
    Console.WriteLine(“你好”+name);
    });
    }
    // delegate就是匿名函数,没有特别说明是哪个函数。(中国、英国说你好)
    lamda表达式:=>goes to
    DelSayHi del=(string name)=>{Console.WriteLine(“你好”+name);};
    del(“zt”);
    ##设计模式
    这里引用刘伟老师的博客,感觉真心棒!
    史上最全设计模式导学目录(完整版)

    cs