.NET第七章

const与readonly

const应该称为常量;

readonly则应称为只读变量。

const、readonly、static readonly定义的常量,指定初始值后(包括在构造函数内指定初始值)将不可更改,可读不可写。

const常量在定义时必须指定初始值,如:private const string str = 1。

readonly和static readonly在声明时可以指定可以不指定初始值,同时也可以在构造函数内指定初始值,如果同时在声明时和构造函数内指定了初始值,以构造函数内指定的值为准。

const和static readonly定义的常量是静态的,只能由类型直接访问;而readonly定义的常量是非静态的,只能由实例对象访问。

static readonly常量,如果在构造函数内指定初始值,则必须是静态无参构造函数。

const可以定义局部常量和字段常量。

const默认是静态的,只能由类型来访问,不能和static同时使用,否则出现编译错误;readonly默认是非静态,由实例对象来访问,可以显式使用static定义为静态成员。

const只能应用在值类型和string类型上,其他引用类型常量只能定义为null;

readonly只读字段,可以是任意类型,但是对于引用类型字段来说,readonly不能限制对该对象实例成员的读写控制。

const必须在字段声明时初始化;而readonly可以在声明时,或者构造函数中进行初始化,不同的构造函数可以为readonly常量实现不同的初始值;static readonly字段只能在声明时,或者静态构造函数中进行初始化。

const可以定义字段和局部变量;readonly只能定义字段。

const定义时必须初始化;readonly定义时可以不进行初始化,但是强烈建议在定义时进行初始化操作。

string类型可以被声明为常量,使得string的值具有只读特性。

const常量将直接被编译到引用程序集中,而readonly则在动态调用时才获取。因此在跨程序集的应用系统中,要特别关注const常量可能引发的不一致问题。

只读属性提供了对只读变量更好的封装性和更灵活的控制机制,同时也能够满足对类型字段只读控制的要求,在使用上,更推荐以只读属性来提供对类型特性的封装。

const是编译时常量,readonly是运行时常量,在应用上,常以static readonly来代替const。

class与struct

class(类)是一种自定义数据结构类型,通常包含字段、属性、方法、构造函数、索引器、操作符等。所有的类都最终继承自System.Object类,new一个类的实例时,对象保存了该实例实际数据的引用地址,而对象的值保存在托管堆中。

struct(结构)是一种值类型,继承自System.ValueType类,struct实例分配在线程的堆栈上,它本身存储了值,而不包含指向该值的指针。

class与struct的区别:

class表现为行为;struct常用于存储数据。

class支持继承,可以继承自类和接口;struct没有继承性,struct不能从class继承,也不能作为class的基类,但支持接口继承。

class可以声明无参构造函数,可以声明析构函数;struct只能声明带参数构造函数,且不能声明析构函数。

实例化时,class要使用new关键字;struct可以不使用new关键字,如果不以new来实例化srtuct,则其所有的字段将处于未分配状态,直到所有字段完成初始化

class可以为抽象类(abstract),可以声明抽象函数;而struct不能为抽象类,也不能声明抽象函数。

class可以声明protected成员、virtual成员、sealed成员和override成员;而struct不可以,但可以重载System.Object的3个虚方法,Equals()、ToString()和GetHashTable()。

class的对象复制分为浅拷贝和深拷贝,必须经过特别的方法来完成复制,而struct创建的对象复制简单,直接以等号连接即可。

class实例由垃圾回收机制来保证内存的回收处理;而struct变量使用完后立即自动解除内存分配。

实现一个主要用于存储数据的结构时,应该考虑struct。其适用于数据量相对小的场合,结构数组具有更高的效率。提供某些和非托管代码通信的兼容性。

特性与属性

特性:attriute

它对程序中的元素进行标注,如类型、字段、方法和属性等。

本质上是一个类,其为目标元素提供关联附加信息。

属性:面向对象编程的基本概念,提供了对私有字段的访问封装,以get和set访问器方法实现对可读可写属性的操作。

特性可以应用的目标元素包括:程序集、模块、类型、属性、事件、字段、方法、参数、返回值。以[,]形式展现,特性间以逗号隔开,如:[AttributeUsage,Flags]

所有自定义的特性名称都应该有个Attribute后缀,这是习惯性约定。所有非抽象

特性必须具有public访问限制。特性常用于编译器指令,突破#define,#undefine,#if,#endif的限制,而且更加灵活。

常用特性:
AttributeUsage特性用于控制如何应用自定义特性到目标元素。
Flags特性用来将枚举数值看作位标记,而非单独的数值。
DllImport特性可以让程序员调用非托管代码。
Serializable特性表明了应用的元素可以被序列化。
Conditional特性用于条件编译,在调试时使用。

接口是包含一组虚方法的抽象类型,其中每一种方法都有其名称、参数和返回值

一个类可以实现多个接口,当一个类实现某个接口时,它不仅要实现该接口定义的所有方法,还要实现该接口从其他接口中继承的所有方法。

抽象类提供多个派生类共享基类的公共定义,它既可以提供抽象方法,也可以提供非抽象方法。抽象类不能实例化,必须通过继承由派生类实现其抽象方法,对抽象类不能使用new关键字,也不能被密封。如果派生类没有实现所有的抽象方法,则该派生类也必须声明为抽象类。实现抽象方法由override方法来完成。

接口与抽象类的相同点:
都不能被直接实例化,都可以通过继承实现其抽象方法。

接口与抽象类的不同点:
接口支持多继承;抽象类不能实现多继承。
接口只能定义抽象规则;抽象类既可以定义规则,还可以提供已实现的成员。
接口是一组行为规范;抽象类是一个不完全的类,着重族的概念。
接口可以用于支持回调;抽象类在实现回调时有局限性。
接口只包含方法、属性、索引器、事件的签名,但不能定义字段和包含实现的方法;抽象类可以定义字段、属性、包含有实现的方法。
接口可以作用于值类型和引用类型;抽象类只能作用于引用类型。

规则与场合:
抽象类应主要用于关系密切的对象,而接口最适合为不相关的类提供通用功能。
接口着重于CAN-DO关系类型,而抽象类则偏重于IS-A式的关系。
接口多定义对象的行为;抽象类多定义对象的属性。
接口定义可以使用public,protected,internal和private修饰符,大部分接口都
被定义为public,方法的访问级别不能低于接口的访问级别。
在由接口增加扩展时,应该增加新的接口,而不能更改现有接口。
尽量将接口设计成功能单一的功能块。
接口名称前面的大写字母“I”是一个约定,字段名以下划线开头。
在接口中,所有的方法都默认为public。
如果预计会出现版本问题,可以创建抽象类。向接口中添加新成员则会强制要求
修改所有派生类,并重新编译,所以版本式的问题最好以抽象类来实现。
从抽象类派生的非抽象必须包括继承的所有抽象方法和抽象访问器的实现。
对抽象类不能使用new关键字,也不能被密封,原因是抽象类不能被实例化。
在抽象方法声明中不能使用static或virtual修饰符。

.NET中类型转换的基本规则:
任何类型都可以安全地转换为其基类类型,可以由隐式转换来完成。
任何类型转换为其派生类型时,必须进行显式转换,转换的规则是:
(类型名) 对象名;
使用GetType可以取得任何对象的精确类型。
基本类型可以使用Convert类实现类型转换。
值类型和引用类型的转换机制称为装箱和拆箱。

is和as操作符

是C#中用于类型转换的,提供了对类型兼容性的判断,从而使得类型转换控制在安全的范畴,提供了灵活的类型转换控制。

is模式:
检查对象类型的兼容性,并返回结果:true或者false。
不会抛出异常。
如果对象为null,则返回值永远为false。
例:o is ISSample;

as模式:
检查对象类型的兼容性,并返回结果,如果不兼容就返回null。不会抛出异常。
如果结果判断为空,则强制执行类型转换将抛出NullReferenceException异常。
as必须和引用类型一起使用。
例:ASSample b = o as ASSample;
等效于 ASSample b = o is ASSample?(ASSample)o:null;

覆写

覆写又称重写,就是在子类中重复定义父类方法,提供不同实现,存在于有继承关系的父子关系中。
在.NET中只有以virtual和abstract标记的虚方法和抽象方法才能被直接
覆写。
覆写以关键字override标记,强调继承关系中对基类方法的重写。
覆写方法要求具有相同的方法签名,包括:相同的方法名、相同的参数
列表和相同的返回值类型。

虚方法:就是以virtual关键字修饰并在一个或多个派生类中实现的方法,子类重写的虚方法则以override关键字标记。一个方法被virtual标记,则不可再被static、abstrcat和override修饰。

抽象方法:就是以abstract关键字修饰的方法,可以看作是没有实现体的虚方法,并且必须在派生类中被覆写,如果一个类包括抽象方法,则该类就是一个抽象类。abstract和virtual一起使用是错误的。

重载:

就是在同一个类中存在多个同名的方法,而这些方法的参数列表和返回值类型不同。

重载存在于同一个类中。

重载方法要求具有相同的方法名,不同的参数列表,返回值类型可以相同也可以不同。

泛型技术,使得相同的参数列表、相同的返回值类型的情况也可以构成重载。

覆写与重载的规则:

如果基类访问引用的是一个抽象方法,则将导致编译错误。

虚方法不能使静态的、密封的。

覆写实现的多态确定于运行时,因此更加的灵活和抽象;重载实现的多态确定于编译时,因此更加简单和高效。二者各有特点和应用,不可相互替代。详细异同见表7-1

override和new的版本控制:

.NET默认为非虚方法,因此必须将基类方法定义为虚方法,以virtual修饰。

在派生类中定义方法为override或者new。使用override表示覆写基类成员,实现派生类自己的版本;使用new表示隐藏基类的虚方法,也就是该方法独立于基类的方法。

派生类方法如果未被定义为override或new,则编译器将发出警告,并默认定义为带new的方法。
在派生类中使用base关键字调用基类方法。

泛型方法的重载与覆写:

如果在基类泛型方法中有约束,则在子类重写的时候,不能加入约束,不能添加新的约束,只能默认继承父类约束。

public void MyFunc (T t);
public void MyFunc (U u);

不能构成泛型方法重载,原因是编译器无法确定泛型类型T和U是否不同,因为实

例化时T和U可能会传入相同的类型。

public void MyFunc (T t) where T : A ;
public void MyFunc (T t) where T : B;

不能构成泛型方法重载,原因同样是在编译时编译器无法确定约束条件中的A和B是否不同,从而也无法在编译时确定这两个方法是否不同。

浅拷贝:指对象的字段被拷贝,而字段引用的对象不会被拷贝,拷贝对象和源对象仅仅是引用名称有所不同,但是它们共用一份实体。对任何一个对象的更改,都会影响到另一个对象。

深拷贝:指对象的字段被拷贝,同时字段引用的对象也进行了拷贝。拷贝对象和源对象相互独立,不共享任何实例数据,修改一个对象不会影响到另一个对象。值类型之间的赋值操作,执行的就是深拷贝。

静态类:

一个类如果只包含静态成员和静态方法,则该类可以定义为静态类,定义方法是给类加上static修饰符。例如:static class MyStatic

静态类只能包含静态成员和静态方法,否则会抛出编译错误;而非静态类既可以包含非静态成员和非静态方法,也可以包含静态成员和静态方法。

静态类不可实例化;非静态类可以实例化。不管是静态类还是非静态类,对静态成员和静态方法的调用都必须通过类来实现。

如果一个类只包含静态成员和静态方法,应该将该类标记为static,并提供私有的构造函数来避免实例创建。

静态构造函数

静态构造函数,用于初始化类中的静态成员,包括静态字段和静态属性。静态构造函数不能带参数、不能有访问修饰符也不能被调用。

静态构造函数,可以和无参的实例构造函数同存。

静态构造函数,只能对静态成员进行初始化操作,不能作用于非静态成员;实例构造函数,可以初始化实例成员,也可以初始化静态成员,但是静态只读字段除外。

静态构造函数只被执行一次;实例构造函数可以再多次实例创建时被执行多次。

一个类只能由一个静态构造函数;一个类可以由多个实例构造函数。

静态字段的初始值,在静态构造函数调用之前被指定。

静态成员可以在声明时初始化,也可以通过静态构造函数进行初始化,这两种初始化都只能被执行一次。

静态成员主要包括静态字段和静态属性,静态成员可以实现类中能够被所有实例对象共享的数据。

静态字段一般实现为private,静态属性一般实现为public。

静态成员和类相关联,不依赖于对象而存在,只能由类访问,而不能由对象访问;实例成员只能由对象实体访问,而不能由类访问。

静态成员属于类所有,无论创建多少实例对象,静态成员在内存中只有一份;实例成员属于类的实例所有,每创建一个实例对象,实例成员都会在内存中分配一块内存区域。

静态方法以static关键字标识。

静态成员不能修饰为virtual,abstract,override

静态方法只能访问静态成员,不能以this访问。

派生类可以访问静态方法,但不能覆写静态方法。

静态方法只能访问静态成员和静态方法,可以间接通过创建实例对象来访问实例成员和实例方法;实例方法可以直接访问实例成员和静态成员,也可以直接访问实例方法和静态方法。

Main方法为静态的,因此Main方法中不能直接访问Main所在类的实例方法和实例成员。

集合

集合的作用:

一个对象容器,并且提供了存取、检索、遍历等基本操作。

以统一的方式处理一组对象,简化对象管理操作。

实现多个返回值,当方法需要返回多个结果时,集合提供了很好的实现方法。

集合的特性:

Add添加
Insert插入
Remove移除
Clear清除所有元素
IndexOf/LastIndexOf在集合中进行线性搜索,如果找到第一个匹配的对象,则返回其索引
Sort对集合元素进行排序
GerEnumerator返回循环访问集合的枚举数
Count获取集合的元素个数
Item获取集合的指定元素

Array和ArrayList:

相同点:
均实现了相同的接口,因此具有很多相同的操作方法,例如对自身进行枚举,能够以foreach语句遍历集合成员等。
创建的对象均存储在托管堆中。

不同点:
Array只能存储同构对象,不过声明为Object类型的数组除外,因为任何类型都可
以隐式转换为Object类型;ArrayList可以存储异构对象,这是因为在本质上ArrayList内部维护着一个object[]items类型字段。
Array可以是一维的,也可以是多维的;ArrayList只能是一维的。
Array的容量是固定的,一旦声明,不可更改;ArrayList的容量则是动态增加的。
Array的下限可以设置,而ArrayList的下限只能为0。
Array只有简单的方法来获取或设置元素值,不能随意增加或者删除数组元素;

ArrayList提供了更丰富的方法来完成对元素的操作,可以方便的插入或者删除任意的元素。
对空数组元素进行操作,可能会引起系统异常,这是数组操作常见的问题之一,
因此,对数组元素操作之前进行是否为NULL的判断十分必要。

Queue和Stack:

都是有序集合类,实现了ICollection接口,Queue是先进先出,Stack是后进先出
两者的默认容量不同,Stack为10,Queue为32。

Queue方法:Enqueue用于向队尾添加元素,Dequeue用于从队列返回并移除最开始的元素,Peek用于从队列返回最开始的元素但不移除。

Stack方法:Push用于在Stack顶部插入元素,Pop用于返回并移除Stack顶部的元素,Peek用于返回Stack顶部的元素但不移除。

Hashtable:是典型的字典类型,实现了IDictionary和ICollection接口。一个键Key用于快速检索集合元素;一个事值Value用于存储用户数据,键值必须是唯一的,其不能为null。

泛型集合:
Dictionary 泛型类对应于Hashtable类。
List 泛型类对应于Arraylist类。
SortedList 泛型类对应于SortedList类。
Queue 泛型类对应于Queue类。
Stack 泛型类对应于Stack类。
Collection 泛型类对应于CollectionBase类。
ReadOnlyCollection 泛型类对应于ReadOnlyCollectionBase类。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值