C#高级编程学习笔记(一)

在公司前辈的指导下,决定再次去巩固C#语法,经过挑选,选择了《C#高级编程》这本书。这本书在对原理性的东西讲解的时候比较透彻,讲的也很深入,对于很多上学时期比较模糊的概念都给出了解答。所以做了很多笔记,贴在这里方便以后进行查阅。

1.引用类型和值类型的区别?
在C#中值类型的变量直接存储数据,而引用类型的变量持有的是数据的引用,数据存储在数据堆中。

值类型(value type):byte,short,int,long,float,double,decimal,char,bool 和 struct 统称为值类型。值类型变量声明后,不管是否已经赋值,编译器为其分配内存。
引用类型(reference type):string 和 class统称为引用类型。当声明一个类时,只在栈中分配一小片内存用于容纳一个地址,而此时并没有为其分配堆上的内存空间。当使用 new 创建一个类的实例时,分配堆上的空间,并把堆上空间的地址保存到栈上分配的小片空间中。
值类型的实例通常是在线程栈上分配的(静态分配),但是在某些情形下可以存储在堆中。引用类型的对象总是在进程堆中分配(动态分配)。
1.值类型:
在C#中,所有被声明为以下类型的事物被称为值类型:
bool 
byte 
char 
decimal 
double 
enum 
float 
int 
long 
sbyte 
short 
struct 
uint 
ulong 
ushort

2.引用类型:
所有的被声明为以下类型的事物被称为引用类型:
class 
interface 
delegate 
object 
string
数组

2.堆和栈的区别?
栈就像装数据的桶或箱子
    我们先从大家比较熟悉的栈说起吧,它是一种具有后进先出性质的数据结构,也就是说后存放的先取,先存放的后取。这就如同我们要取出放在箱子里面底下的东西(放入的比较早的物体),我们首先要移开压在它上面的物体(放入的比较晚的物体)。

堆像一棵倒过来的树
而堆就不同了,堆是一种经过排序的树形数据结构,每个结点都有一个值。通常我们所说的堆的数据结构,是指二叉堆。堆的特点是根结点的值最小(或最大),且根结点的两个子树也是一个堆。由于堆的这个特性,常用来实现优先队列,堆的存取是随意,这就如同我们在图书馆的书架上取书,虽然书的摆放是有顺序的,但是我们想取任意一本时不必像栈一样,先取出前面所有的书,书架这种机制不同于箱子,我们可以直接取出我们想要的书。
栈负责保存我们的代码执行(或调用)路径,而堆则负责保存对象(或者说数据,接下来将谈到很多关于堆的问题)的路径。


3.ref功能: ref 关键字使参数按引用传递。其效果是,当控制权传递回调用方法时,在方法中对参数所做的任何更改都将反映在该变量中。简单点说就是,使用了ref和out的效果就几乎和C中使用了指针变量一样。它能够让你直接对原数进行操作,而不是对那个原数的Copy进行操作。

4.out参数:out是内部为外部变量赋值,out一般用在函数需要有多个返回值的场所
           1.如果方法用参数out修饰了,那么参数在方法外也要用out修饰
           2. 如果方法的参数用out修饰了,那么这个参数必须在方法里进行赋值
           3.当参数在方法外进行赋值时,参数out把方法里的值传到了方法外,所以最后参数值为在方法里赋的值

5.方法的重载:
 方法重载是指在一个类中定义多个同名的方法,但要求每个方法具有不同的参数的类型或参数的个数。调用重载方法时,编译器能通过检查调用的方法的参数类型和个数选择一个恰当的方法。方法重载通常用于创建完成一组任务相似但参数的类型或参数的个数不同的方法。方法重载是让类以统一的方式处理不同类型数据的一种手段。调用方法时通过传递给它们的不同个数和类型的参数来决定具体使用哪个方法..

6.构造函数:
 构造函数,是一种特殊的方法。主要用来在创建对象时初始化对象,即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中。特别的一个类可以有多个构造函数,可根据其参数个数的不同或参数类型的不同来区分它们即构造函数的重载。写一个类,如果没有写任何的构造函数,那么这个类有一个默认的无参数的构造函数。如果写了构造函数,那么在这个类中就有两个构造函数,一个默认的,一个是自己写的,不过,自己写了构造函数,之前默认的那个构造函数就不能用了,如果还想用之前的那个默认的构造函数,就必须再重新写一个无参数的构造函数。

7.静态构造函数:
静态构造函数是在构造函数方法前面添加了static关键字之后形成的,并且没有修饰符(public,private),没有参数。
类有一些静态字段或属性,需要在第一次使用之前从外部源中初始化这些静态字段或属性。

8.var关键字
 var可以理解为匿名类型,我们可以认为它是一个声明变量的占位符。它主要用于在声明变量时,无法确定数据类型时使用。
使用var定义变量时有以下四个特点:
1. 必须在定义时初始化。也就是必须是var s = “abcd”形式,而不能是如下形式: var s; s = “abcd”;
2. 一但初始化完成,就不能再给变量赋与初始化值类型不同的值了。
3. var要求是局部变量。
4. 使用var定义变量和object不同,它在效率上和使用强类型方式定义变量完全一样。

9.C#如何封装:
面向对象的三个基本特征是封装、继承、多态。
概念:封装是实现面向对象程序设计的第一步,封装就是将数据或函数等集合在一个个的单元中(我们称之为类)。被封装的对象通常被称为抽象数据类型。
意义:封装的意义在于保护或者防止代码(数据)被我们无意中破坏。防止对实现细节的访问。
我们只是提供调用类的方法,而调用者不必了解到类内部怎样处理相关数据。
C#中通常将方法或者其他数据成员封装在一个类中,具体地,封装使用访问修饰符来实现。一个访问修饰符定义了一个类成员的范围和可见性。
访问修饰符:
public、private、protected、internal、protected internal

10:结构是值类型不是引用类型,它们存储在栈中或存储为内联其生存期的限制与简单的数据类型一样。但在语法上常常当作类来处理。
结构是值类型,所以会影响性能,但根据使用结构的方式,这种影响可能是正面的,也可能是负
面的。正面的影响是为结构分配内存时,速度非常快,因为它们将内联或者保存在堆栈中。
在结构超出了作用域被删除时,速度也很快。另一方面,只要把结构作为参数来传递或者把一个结构
赋给另一个结构(例如A=B,其中A 和B 是结构),结构的所有内容就被复制,而对于类,则只复制
引用。这样,就会有性能损失,根据结构的大小,性能损失也不同。注意,结构主要用于小的数据结
构。但当把结构作为参数传递给方法时,就应把它作为ref 参数传递,以避免性能损失--此时只传递
了结构在内存中的地址,这样传递速度就与在类中的传递速度一样快了。另一方面,如果这样做,就
必须注意被调用的方法可以改变结构的值。
禁止在结构内使用无参数的构造函数。

11.重载和重写的区别:
重写是子类的方法覆盖父类的方法,要求方法名和参数都相同;一般用于子类继承父类时重写父类中的方法。

重载是在同一个类中的两个或两个以上的方法,拥有相同的方法名,但是参数却不相同,方法体也不相同,最常见的
重载的例子就是类的构造函数

12.pattial部分类
Partial是局部类型的标志。局部类型可以实现将一个类、结构或接口分成几个部分,分别放在在几个不同的.cs文件中(当然也可以放在同一个.cs文件中)。
在程序进行编译之后,将会合并成一个完整的类。因此局部类型并没有看起来那么难以理解,使用partial只是让类变得更容易管理,实际使用时和普通的类一样。
在嵌套的类型中,只要partial关键字位于class关键字前面就可以嵌套部分类,把部分类编译到内存中,属性接口等会合并。

13.ToString():
在类型中声明为虚方法,想要输出自己想要的字符串要在自己的类中重写tostring()。

14.扩展方法:
允许改变一个类但不需要该类的源代码,扩展方法是静态方法,它是类的一部分。对于扩展方法,第一个参数是
要扩展的类型,放在this关键字后面。

15.实现继承和接口继承:
实现继承表示一个类型派生于一个基类型,它拥有该类型的所有成员字段和函数。
接口继承表示只继承了函数的签名,没有继承任何实现的代码。
C#不支持多重实现继承,但支持多重接口继承。

16.虚方法:
把一个基类函数声明为virtual,就可以在任何派生类中重写该函数。也可以把属性声明为virtual

17.隐藏方法:
如果签名相同的方法在基类和派生类中都进行了声明,但是该方法没有分别声明为virtual和override
,派生类就会隐藏基类方法。隐藏方法会造成对于给定类的实例调用错误方法的危险。

18.base<methodname>.()可以从派生类中调用方法的基类版本。

19.抽象类和抽象函数:
抽象类实质是对象的抽象,它只能用作基类,是对继承子类的对象的抽象;
而接口实质是继承的子类定义了一个必须完成的功能清单的定义统一接口规范强制功能清单约束。
两者都是不能被实例化的,只是一个定义,由继承的子类来完成,

abstract修饰符可以和类、方法、属性、索引器及事件一起使用,在类声明中使用abstract修饰符以表明这个类只能是其他类的基类。
抽象类的特性

(1)抽象类不能被实例化
(2)抽象类可以包含抽象方法和抽象访问器
(3)不能用sealed修饰符修改抽象类,因为抽象类本身就是用来给其他类继承的
(4)抽象类的非抽象子类必须实现其继承的所有抽象方法和抽象访问器

抽象方法
(1)抽象方法是隐式的虚方法
(2)抽象方法只允许声明在抽象类中
(3)抽象方法不能提供实际的实现,所以没有方法体;抽象方法的实现是在非抽象的派生类中以override重写实现的
(4)抽象方法声明中不可以使用static或者virtual修饰符
(5)abstract关键字不能修饰静态方法或静态属性

抽象类的构造函数
(1)不要再抽象类中定义public或protected internal访问权限的构造函数
(2)应在抽象类中定义protected或private访问权限的构造函数
(3)如果在抽象类中定义一个protected构造函数,则在实例化派生类时,基类可以执行初始化任务

抽象方法和虚方法的区别
虚方法有实现部分,并且派生类对其重写是可选的;抽象方法没有实现部分,并且强制非抽象派生类对其重写

20.构造函数:构造函数的调用顺序是先调用system.object,在按照层次结构由上向下进行
直到编译器到达要实例化的类为止。

21.接口:
接口的定义是指定一组函数成员而不实现成员的引用类型,其它类型和接口可以继承接口。定义还是很好理解的,但是没有反映特点,接口主要有以下特点:
(1)通过接口可以实现多重继承,C#接口的成员不能有public、protected、internal、private等修饰符。原因很简单,接口里面的方法都需要由外面接口实现去实现方法体,那么其修饰符必然是public。C#接口中的成员默认是public的,java中是可以加public的。
(2)接口成员不能有new、static、abstract、override、virtual修饰符。有一点要注意,当一个接口实现一个接口,这2个接口中有相同的方法时,可用new关键字隐藏父接口中的方法。
(3)接口中只包含成员的签名,接口没有构造函数,所有不能直接使用new对接口进行实例化。接
口中只能包含方法、属性、事件和索引的组合。接口一旦被实现,实现类必须实现接口中的所有成员,除非实现类本身是抽象类。
(4)C#是单继承,接口是解决C#里面类可以同时继承多个基类的问题。

22.接口和抽象类的区别
接口用于规范,抽象类用于共性。抽象类是类,所以只能被单继承,但是接口却可以一次实现多个。
接口中只能声明方法,属性,事件,索引器。而抽象类中可以有方法的实现,也可以定义非静态的类变量。
抽象类可以提供某些方法的部分实现,接口不可以。抽象类的实例是它的子类给出的。接口的实例是实现接口的类给出的。
在抽象类中加入一个方法,那么它的子类就同时有了这个方法。而在接口中加入新的方法,那么实现它的类就要重新编写(这就是为什么说接口是一个类的规范了)。
接口成员被定义为公共的,但抽象类的成员也可以是私有的、受保护的、内部的或受保护的内部成员(其中受保护的内部成员只能在应用程序的代码或派生类中访问)。此外接口不能包含字段、构造函数、析构函数、静态成员或常量。
我们在VS中实现接口时会发现有2个选项,一个是实现接口,一个是显示实现接口。实现接口就是我们平常理解的实现接口,而显示实现接口的话,实现的方法是属于接口的,而不是属于实现类的。

23.泛型:
性能 是泛型的主要优点 另一个特性是类型安全。泛型类型T定义了允许使用的类型。
泛型允许更好的重用二进制代码。
对值类型使用非泛型集合类,在把值类型与引用类型的互相转换时,需要进行装箱和拆箱操作。

24.装箱和拆箱:
装箱:将值类型(如 int ,或自定义的值类型等)转换成 object 或者接口类型的一个过程。当 CLR 对值类型进行装箱时,会将该值包装为 System.Object 类型,再将包装后的对象存储在堆上。 拆箱就是从对象中提取对应的值类型的一个过程。
装箱是隐式的;拆箱必定是显式的,在拆箱时需要使用类型强制转换运算符。

25.default 关键字 :
用于 switch 语句或默认值表达式中
生成类型的默认值。对于引用类型,将为NULL;对于值类型将为零;对于结构,将为0位模式。这个通常和泛型一起使用。

26.where 用法:
where子句的一个重要限制是,不能定义必须由泛型类型实现的运算符,运算符不能在接口中定义。在where子句中,只能定义基类,接口和默认的构造函数。

27.数组:使用引用类型
除了能声明预定义类型的数组,还可以声明自定义类型的数组,也可以对自定义类型使用数组初始化器。

28.锯齿数组
锯齿数组的大小设置比较灵活,每一行都可以有不同的大小。
在声明锯齿数组时,要依次放置左右括号,在第一对方括号中设置数组包含的行数,第二个方括号设置为空。
迭代锯齿数组中所有元素的代码可以放在嵌套的for循环中,在外层的for循环中迭代每一行,在内层的for循环中迭代这一行中的每一个元素。

29.数组协变:
数组这种与原始类型转换方向相同的可变性就称作协变(covariant)。
数组支持协变,这表示数组可以声明为基类,其派生类型的元素可以赋予数组元素

30.比较运算符(= =)
最好将比较运算符看作严格的值的比较和严格的引用比较之间的中间选项。

《C#高级编程》一共有1500多页,所以笔记会有点多,所以决定每30个知识点为一组进行纪录。

猜你喜欢

转载自blog.csdn.net/qq_35711014/article/details/81463122