你必须知道的.net学习笔记
来源:互联网 发布:淘宝潮女装店铺排行榜 编辑:程序博客网 时间:2024/05/16 09:46
.net 中类型转换的基本规则如下:
1,任何类型都可以安全的转换为其基本类型,可以由隐士转换来完成。
2,任何类型转换为其派生类型时,必须进行显示转换,转换规则是:(类型名)对象名。
3,使用GetType可以取得任何对象的精确类型。
4,基本类型可以使用Convert类实现类型的转换。
5,除了String类型以外的其他类型都有Parse方法,用于将字符串类型转换为对应的基本类型。
6,值类型和引用类型的转换机制成为装箱和拆箱。
面向对象的思想:多态 接口 抽象类
注意:C#中不能包含任何静态成员。一个类可以实现多个接口,当一个类集成某个接口时,它不仅需要实现该接口定义的所有方法,还要实现该接口从其他接口中继承的所有方法。
接口和抽象类的相同点和不同点
相同点:1,都不能被直接实例化,都可以通过集成实现其抽象方法。
2,都是面向抽象编程的技术基础,实现了诸多的设计模式。
不同点:1,接口支持多继承,抽象类不能实现多继承。(多继承:一个子类可以有多个父类,即派生类有多个基类)
2,接口只能定义抽象规则;抽象类既可以定义规则,还可能提供已实现的成员。
3,接口是一组行为规范;抽象类是一个不完全的类
4,接口可以用于支持回调;抽象类不能实现回调,因为继承不支持回调。
5,接口只包含方法、属性、索引器、事件的签名,但不能定义字段和包含实现的方法。
抽象类可以定义字段、属性、包含有实现的方法。
6,接口可以作用于值类型和引用类型;抽象类只能作用于引用类型。
面向对象思想的一个最重要的原则就是:面向接口编程。
23个设计模式中的精髓就是:面向抽象编程。
抽象类应主要用于关系密切的对象,而接口最适合为不相关的类提供通用功能。
接口定义可以使用public,protected,internal和private修饰符,但是几乎所有的接口都定义为public.
在由接口增加扩展时,应该增加新的接口,而不能更改现有接口。
接口名称前面的大写字母I 是一个页顶,正如字段名以下划线开头一样,请坚持这些原则。
版本式问题最好以抽象类来实现。
对抽象类不能使用new关键字,也不能被密封,原因是抽象类不能被实例化。
在抽象方法声明中不能使用static或virtual修饰符。
1,定义抽象类
public abstract class Animal
{
protected string _name;
//声明抽象属性
publicabstract string Name
{
get;
}
//声明抽象方法
publicabstract void Show();
//实现一般方法
publicvoid MakeVoice()
{
Console.WriteLine("Allanimals can make voice");
}
}
2定义接口
publicinterface IAction
{
voidMove();
}
3,实现抽象类和接口
publicclass Duck:Animal,IAction
{
publicDuck(string name)
{
_name=name;
}
//重载抽象方法
publicoverrride void Show()
{
Console.WriteLine(_name+"isshowing for you");
}
publicoverrride string Name
{
get{return_name};
}
publicvoid Move()
{
Console.WriteLine("Duckalso can swim");
}
}
publicclass Dog:Animal,IAction
{
publicDog(string name)
{
_name=name;
}
//重载抽象方法
publicoverrride void Show()
{
Console.WriteLine(_name+"isshowing for you");
}
publicoverrride string Name
{
get{return_name};
}
publicvoid Move()
{
Console.WriteLine("Dogalso can swim");
}
}
4客户端实现
publicclass Test Animal
{
publicstatic void Main(string[] args)
{
Animalduck=new Duck("Duck");
duck.MakeVoice();
duck.show();
Animaldog=new Dog("dog");
dog.MakeVoice();
dog.show();
IActiondogAction=new Dog("A big dog");
dogAction.Move();
}
}
dunai认为:抽象类是提取具体类的公因式,而接口是为了将一个不行管的类‘杂凑’成一个公共的群体。
Artech认为:所有代码公用和可扩展性考虑,尽量使用Abstract Class. 当然接口在其他方面的优势,也不可忽视
shenfx认为:当在差异较大的对象间寻求功能上的共性时,使用接口;当在共性较多的对象间寻求功能上的差异时,使用抽象基类
MSDN的建议:
1,如果预计要创建组件的多个版本,则创建抽象类。抽象类提供简单易行的方法来控制组件版本。通过更新基类,所有继承类都随更改自动更新。另一方面,接口一旦创建就不能更改。如果需要接口的新版本,必须创建一个全新的接口。
2,如果创建的功能将在大范围的全异对象间使用,则使用接口。抽象类应主要用于关系密切的对象,而接口最适合为不相关的类提供通用功能。
3,如果要设计小而简练的功能块,则使用接口。如果要设计大的功能单元,则使用抽象类。抽象类允许部分实现类,而接口不包含任何成员的实现。
4,如果要在组件的所有实现间提供通用的已实现的功能,则提供抽象类。抽象类允许部分实现类,而接口不包含任何成员实现。
特性和属性
定制特性attribute,本质上是一个类,其为目标元素提供关联附加信息,并在运行期以反射方式来获取附加信息
属性是面向对象编程的基本概念,提供了对私有字段的访问封装。在C#中以get和set访问器方法实现对可读可写属性的操作,提供了安全和灵活的数据访问封装。
1,定制特性类型,必须直接或者间接的继承自System.Attribute类,而且该类型必须有公共构造函数来创建其实例。
2,定制特性也可以应用在其他定制特性上
3,定制特性不会影响应用元素的任何功能,只是约定了该元素具有的特质。
4,所有非抽象特性必须具有public访问限制。
5,特性常用于编译器指令,突破#define,#undefine,#if,#endif的限制,而且更加灵活。
6,定制特性常用于运行期获得代码注释信息,以附加信息来优化调试。
7,定制特性可以应用在某些设计模式中,如工厂模式。
AttributeUsage
Flags以Flags特性来讲枚举数值看做为标记,而非单独的数值。
enumAnimal{
Dog=0x0001,
Cat=0x0002,
Duck=0x0004,
Chicken=0x0008
}
Animalanimals =Animal.Dog | Animal.Cat;
Console.WriteLine(animals.ToString());
//定义特性
[AttributeUsage(AttributeTargets.Class|AttributeTargets.Method,Inherited=true)]
publicclass TestAttribute:System.Attribute
{
publicTestAttribute(string message)
{
Console.WriteLine(message);
}
publicvoid RunTest()
{
Console.WriteLine("TestAttributehere.");
}
}
//应用于目标元素
[Test("ErrorHere")]
publicvoid CannotRun()
{
}
//如果没有什么机制来再运行期来获取Attribute的附加信息,那么attribute就没有什么存在的意义。因此,.net中以反射
机制来实现在运行期获取attribute信息,实现方法如下:
publicstatic void Main()
{
Testert=new Tester();
t.CannotRun();
Typetp=typeof(Tester);
MethodInfomInfo=tp.GetMethod("CannotRun");
TestAttributemyAtt=(TestAttribute)Attribute.GetCustomAttribute(mInfo,typeof(TestAttribute));
myAtt.RunTest();
}
MSDN认为:特性描述如何将数据序列化,指定用于强制安全性的特性,并限制实时(JIT)编译器的优化,从而使代码易于调试。属性还可以记录文件名或者代码作者,或在窗体开发阶段控制控件和成员的可见性。
后来居上 class 和struct
struct英雄迟暮,class天下独行,最本质的区别是class是引用类型,而struct是值类型,它们在内存中的分配情况有所区别。
.net中,所有的类最终继承自System.Object类,一次是一种引用类型,也就是说,new一个类的实例时,对象保存了该实例实际数据的引用地址,而对象的值保存在托管堆中(managed heap)中。
struct(结构)是一种值类型,用于将一组相关的信息变量组织为一个单一的变量实体。所有的结构都继承自System.ValueType
struct实例分配在线程的堆栈(strack)上,它本身存储了值,而不包含只想该值的指针。所以在使用struct时,我们可以将其当做int、char这样的基本类型对待。
相同点:语法类似
不同点:1,class是引用类型,继承自System.Object类;struct是值类型,继承自System.ValueType类,因此不具多态性。但是注意,System.ValueType是个引用类型。
2,从职能观点来看,class表现为行为;而struct常用于存储数据。
3,class支持继承,可以继承自类和接口;而struct没有继承性,struct不能从class继承,也不能作为class的基类,但struct支持接口继承。
4,class可以声明无参数构造函数,可以声明析构函数;而struct只能声明带参数构造函数,且不能析构函数。因此,struct没有自定义的默认无参构造函数,默认无参构造⑦只是简单地把所有值初始化为他们得0等价值。
5,实例化时,class要使用new关键字;而struct可以不使用new关键字,如果不以new来实
例化struct,则其所有的字段将处于未分配状态,直到所有字段完成初始化,否则引用未赋值的字段会导致编译错误。
6,class可以实抽象类(abstract),可以声明抽象函数;而struct为抽象,也不能声明抽象函数
7,class可以声明protected成员、virtual成员、sealed成员和override成员;而struct不可以,但是值得注意的是,
struct可以重载System.Object的3个虚方法,Equals()、ToString()和GetHashTable()
8,class的对象复制分为浅拷贝和深拷贝(该主题我们在本系列以后的主题中将重点讲述,本文不作详述),必须经过特别的方法来完成复制;而struct创建的对象复制简单,可以直接以等号连接即可。
9,class可以实抽象类(abstract),可以声明抽象函数;而struct为抽象,也不能声明抽象函数
我们可以简单的理解,class是一个可以动的机器,有行为,有多态,有继承;而struct就是个零件箱,组合了不同结构的零件。其实,class和struct最本质的区别就在于class是引用类型,内存分配于托管堆;而struct是值类型,内存分配于线程的堆栈上。由此差异,导致了上述所有的不同点,所以只有深刻的理解内存分配的相关内容,才能更好的驾驭。本系列将再以后的内容中,将引用类型和值类型做以深入的比较和探讨,敬请关注。当然正如本文标题描述的一样,使用class基本可以替代struct的任何场合,class后来居上。虽然在某些方面struct有性能方面的优势,但是在面向对象编程里,基本是class横行的天下。
至少在以下情况下,鉴于性能上的考虑,我们应该考虑使用struct来代替class:
1,任何类型都可以安全的转换为其基本类型,可以由隐士转换来完成。
2,struct变量占有堆栈的空间,因此只适用于数据量相对小的场合
3,结构数组具有更高的效率
4,提供某些和非托管代码通信的兼容性
(1)定义接口
interfaceIPerson
{
voidGetSex();
}
(2)定义类
publicclass Person
{
publicPerson()
{
}
publicPerson(string name,int age)
{
_name=name;
_age=age;
}
privatestring _name;
publicstring Name
{
get{return_name;}
set{_name=value;}
}
privateint _age;
publicint Age
{
get{return_age;}
set{_age=value;}
}
}
(3)定义结构
//可以继承自接口,不可继承类或结构
struct Family:IPerson
{
publicstring name;
public int age;
publicbool sex;
publicstring country;
publicPerson person;
//不可包含显示无参构造函数和析构函数
publicFamily(string name,int age,bool sex,string country,Person person)
{
this.name=name;
this.age=age;
this.sex=sex;
this.country=country;
this.person=person;
}
//不可以实现protected virtual sealed 和override成员
publicvoid GetSex()
{
if(sex)
{
Console.WriteLine();
}
else
{Console.WriteLine();
}
}
publicvoice ShowPerson()
{
Console.WriteLine();
}
publicoverrride string ToString()
{
returnString.Format("{0} is {1}, {2} from {3}", person.Name, age, sex ?"Boy" : "Girl", country);
}
}
(4)测试结构和类
classMyTest
{
staticvoid Main(string[] args)
{
//不使用new来生成结构,其内部成员将初始化为0
FamilynewFamily;
newFamily.name="Anytao Family";
newFamily.sex=true;
//以new来生成结构,调用带参数构造器
FamilymyFamily = new Family("anytao",26,true,"china",newPersong());
Personperson =new person();
person.Name="anytao";
//按值传递参数
ShowFamily(myFamily);
//按引用传递参数
ShowPerson(person);
myFamily.GetSex();
myFamily.ShowPerson();
}
publicstatic void ShowPerson(Person person)
{
person.name="Name";
Console.WriteLine();
}
publicstatic void ShowFamily(Family family)
{
family.name="Aeor";
Console.WriteLine();
}
}
深入浅出关键字 把new说透
对new的典型解释:
1,作为运算符,用于创建对象和调用构造函数。
2,作为修饰符,用于向基类成员隐藏继承成员。
作为修饰符,基本的规则可以总结为:实现派生类中隐藏方法,则基类方法必须定义为virtual;new作为修饰符,实现隐藏基类成员时,不可和override共存,原因是这两者语义相斥:new用于实现创建一个新成员,同时隐藏基类的同名成员;而override用于实现对基类成员的扩展。
3,作为约束,用于在泛型声明中约束可能用作类型参数的参数的类型。
new
1,new一个class时,new完成了一下两个方面的内容:一是调用newobj命令来为实例在托管堆中分配内存;二是调用构造函数
来实现对象初始化。
2,new一个struct时,new运算符用于调用其带构造函数,完成实例的初始化。
3,new一个int时,new运算符用于初始化其值为0.
必须清楚,值类型和引用类型在分配内存时是不同的,值类型分配于线程的堆栈(stack)上,并变量本身就保存其实值,因此也不受GC的控制;而引用类型变量,包含了只想托管堆的应用。
4,new运算符不可重载。
5,new分配内存失败,将引发OutOFMemoryException异常。
面向对象原则:多组合,少继承;低耦合,高内聚;
让ATM告诉你,什么是封装
1,字段通畅定义为private,属性通常实现为public,而方法在内部实现为private,对外部实现为public,从而保证对内部数据的可靠性读写控制,保护了数据的安全和可靠,同时又提供了与外部接口的有效交互。
2,我们理解的封装,应该是以实现UI分离为目的的软件设计方法,一个系统或者软件开发之后,从维护和升级的目的考虑,一定要保证对外接口部分的绝对稳定。
OO智慧中的封装性旨在保证:
— 隐藏系统实现的细节,保证系统的安全性和可靠性。
— 提供稳定不变的对外接口。因此,系统中相对稳定部分常被抽象为接口。
— 封装保证了代码模块化,提高了软件的复用和功能分离。
1.将相似的类抽象出公共基类,在基类中实现具有共性的特征,并由子类继承父类的特征,例如Word、PDF、TXT的基类可以抽象为DocLoader;而JPG和GIF的基类可以抽象为ImageLoader,这种实现体现的是面向对象的开放封闭原则:对扩展开放,对修改关闭。如果有新的类型需要扩展,则只需继承合适的基类成员,实现新类型的特征代码即可。
2.实现可柔性扩展的接口机制,能够更加简单的实现增加新的文件类型加载程序,也能够很好的扩展打开文件之外的其他操作,例如删除、重命名等修改操作。
3,实现在不需要调整原系统,或者很少调整原系统的情况下,进行功能扩展和优化,甚至是无需编译的插件式系统。
首先是通用的接口定义:
interfaceIFileOpen { void Open(); }
接着定义所有文件类型的公共基类 因为公共的文件基类是不可以实例化的,在此处理为抽象类实现会更好。
abstractclass Files: IFileOpen {
privateFileType fileType = FileType.doc;
publicFileType FileType {
get{ return fileType; }
}
publicabstract void Open();
}
有了文件类型的公共基类,是时候实现其派生类了。
abstractclass DocFile: Files {
publicint GetPageCount() { //计算文档页数}
}
abstractclass ImageFile : Files {
publicvoid ZoomIn() { //放大比例 }
publicvoid ZoomOut() { //缩小比例 }
}
实现具体资料类
classWORDFile : DocFile {
publicoverride void Open()
{Console.WriteLine("Open the WORD file."); }
}
一个资料管理类来进行资料的统一管理
classLoadManager {
privateIList<Files> files = new List<Files>();
publicIList<Files> Files {
get{ return files; }
}
publicvoid LoadFiles(Files file) {
files.Add(file);
}
//打开所有资料
publicvoid OpenAllFiles() {
foreach(IFileOpenfile in files) {
file.Open();
}
}
//打开单个资料
publicvoid OpenFile(IFileOpen file) {
file.Open();
}
//获取文件类型
publicFileType GetFileType(string fileName) {
//根据指定路径文件返回文件类型
FileInfofi = new FileInfo(fileName);
return(FileType)Enum.Parse(typeof(FileType),fi.Extension);
}
}
简单的客户端,并根据所需进行文件的加载:
classFileClient {
publicstatic void Main() {
//首先启动文件加载器
LoadManagerlm = new LoadManager();
//添加要处理的文件
lm.LoadFiles(newWORDFile());
lm.LoadFiles(newPDFFile());
lm.LoadFiles(newJPGFile());
lm.LoadFiles(newAVIFile());
foreach(Files file in lm.Files)
{if (file is 爷爷选择的)
//伪代码
{lm.OpenFile(file); }
}
}
}
爷爷机子上的资料又增加了新的视频文件MPEG
首先是增加处理MPEG文件的类型MPEGFile,并让它继承自MediaFile,实现具体的Open方法即可。
classMPEGFile : MediaFile {
publicoverride void Open()
{Console.WriteLine("Open the MPEG file."); }
}
接着就是添加处理新文件的加载操作,如下:
lm.LoadFiles(newMPEGFile());
多态的类型、本质和规则
多态分为四类:强制的、重载的、参数的和包含的
从面向对象的角度来看,根据其实现的方式我们可以进一步分为基类继承式多态和接口实现式多态。
FilesmyFile = new WORDFile();
myFile.Open();
myFile是一个父类Files变量,保持了指向子类WORDFile实例的引用
IFileOpenmyFile = new WORDFile();
myFile.Open();
多态的运行机制
静态绑定在编译期就可以确定关联,一般是以方法重载来实现的;
动态绑定则在运行期通过检查虚拟方法表来确定动态关联覆写的方法,一般以继承和虚方法来实现
在.NET中,虚方法以virtual关键字来标记,在子类中覆写的虚方法则以override关键字标记。
严格来讲,.NET中并不存在静态绑定
一个接口,多种方法
在.NET中,默认情况下方法是非虚的,以C#为例必须显式地通过virtual或者abstract标记为虚方法或者抽象方法,以便在子类中覆写父类方法。
IL intermidate language
数据库字段类型
get post 提交方式
页面传值
session cookie
异常基类
存储过程的优点与缺点
SOA
垃圾回收机制 Gabage Collection
cookie数据存放在客户的浏览器上,session数据放在服务器上。
cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗考虑到安全应当使用session。
session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能考虑到减轻服务器性能方面,应当使用COOKIE。
单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。
Getpost 区别
1. GET使用URL或Cookie传参。而POST将数据放在BODY中。
2. GET的URL会有长度上的限制,则POST的数据则可以非常大。
3. POST比GET安全,因为数据在地址栏上不可见。
Typeof操作符,则常在反射时,获得自定义类型的Type对象,从而获取关于该类型的方法、属性等
值类型和引用类型在内存中的分配区别是决定其应用不同的根本原因,由此我们就可以很容易的解释为什么参数传递时,按值传递不会改变形参值,而按址传递会改变行参的值,道理正在于此.
值类型的内存不由GC控制,作用域结束时,值类型会自行释放,减少了托管堆的压力,因此具有性能上的优势.
通常struct比class更高效
而引用类型的内存回收,由GC来完成,微软甚至建议用户最好不要自行释放内存
值类型是密封的(sealed),因此值类型不能作为其他任何类型的基类,但是可以单继承或者多继承接口;而引用类型一般都有继承性。
值类型不具有多态性;而引用类型有多态性
值类型变量不可为null值,值类型都会自行初始化为0值
而引用类型变量默认情况下,创建为null值,表示没有指向任何托管堆的引用地址
对值为null的引用类型的任何操作,都会抛出NullReferenceException异常
==,未重载的==的值类型,将比较两个值是否“按位”相等
==,默认为引用地址比较,通常进行实现了==的重载,未重载==的引用类型将比较两个对象是否引用地址,等同于引用类型的Equals方法。因此,很多的.NET类实现了对==操作符的重载,例如System.String的==操作符就是比较两个字符串是否相同。而==和equals方法的主要区别,在于多态表现上,==是被重载,而Equals是重写。
关于垃圾回收,对其有以下几点小结:
1.CLR提供了一种分代式、标记清除型GC,利用标记清除算法来对不同代龄的对象进行垃圾收集和内存紧缩,保证了运算效率和执行优化。
2.一个对象没有被其他任何对象引用,则该对象被认为是可以回收的对象
3.最好不要通过调用GC.Collect来强制执行垃圾收集.
4.垃圾对象并非立即被执行内存清理,GC可以在任何时候执行垃圾收集
base和this示例
1, base常用于,在派生类对象初始化时和基类进行通信。
2. base可以访问基类的公有成员和受保护成员,私有成员是不可访问的。
3. this指代类对象本身,用于访问本类的所有常量、字段、属性和方法成员,而且不管访问元素是任何访问级别。因为,this仅仅局限于对象内部,对象外部是无法看到的,这就是this的基本思想。另外,静态成员不是对象的一部分,因此不能在静态方法中引用this。
4,尽量少用或者不用base和this
5.在静态成员中使用base和this都是不允许的
6.base是为了实现多态而设计的
Dispose模式是.NET提供的一种显式清理对象资源的约定方式,用于在.NET 中释放对象封装的非托管资源
因为非托管资源不受GC控制,对象必须调用自己的Dispose()方法来释放,这就是所谓的Dispose模式
覆写,又称重写,就是在子类中重复定义父类的方法,提供不同的实现,存在于有继承关系的父子关系。
总结覆写的基本特征包括:
1.在.NET中只有以virtual和abstract标记的虚方法和抽象方法才能被直接覆写。
2.覆写以关键字override标记,强调继承关系中对基类方法的重写。
3.覆写方法要求具有相同的方法签名,包括:相同的方法名、相同的参数列表和相同的返回值类型。
重载:在同一个类中存在多个同名的方法,而这些方法的参数列表和返回值类型不同。方法的地址,在编译器就已经确定,是一种静态绑定。
1.重载存在于同一个类中。
2.重载方法要求具有相同的方法名,不同的参数列表,返回值类型可以相同也可以不同(通过operator implicit 可以实现一定程度的返回值重载,不过不值得推荐)。
?
虚方法:就是以virtual关键字修饰并在一个或多个派生类中实现的方法,子类重写的虚方法则以override关键字标记。虚方法调用,是在运行时确定根据其调用对象的类型来确定调用适当的复写方法。.net中默认是非虚方法,如果一个方法被virtual标记,则不可再被static abstracat 和override修饰。
抽象方法:是以abstract关键字修饰的方法,抽象方法可以看做是没有实现体的虚方法,并且必须在派生类中被复写,如果一个类中包括抽象方法,则该类就是一个抽象类。因此,抽象方法其实隐含为虚方法,只是在声明和调用语法上有所不同。
。net中复写实现了运行时的多态性,而重载则实现了编译时的多态性。
规则而定:对象判等 值相等 引用相等
操作符“==”在值类型情况下表示是否值相等,由值类型的根类System.ValueType提供了实现;而在引用类型情况下表示是否引用相等,
Equals虚方法与==操作符的主要区别在于多态表现:Equals通过虚方法覆写来实现,而==操作符则是通过运算符重载来实现
1,值相等还是引用相等决定于具体的需求,Equals方法的覆写实现也决定于类型想要实现的判等逻辑
2,几个判等方法相互引用,所以对某个方法的覆写可能会影响其他方法的执行结果
3,如果覆写了Equals虚方法,则必须重新实现GetHashCode方法,使二者保持同步
4,禁止从Equals方法或者“==”操作符抛出异常,应该在Equals内部首先避免null引用异常,要么相等要么不等。
5,ReferenceEquals方法比较两个System.String类型的唯一性时,要注意String类型的特殊性:字符串驻留。
String究竟特殊在哪里
1,创建特殊性:String对象不以newobj指令创建,而是IDstr指令创建。在实现机制上,CLR给了特殊照顾来优化其性能。
2,String类型是.net中不变模式的经典应用,在CLR内部由特定的控制器来专门处理String对象。
3,应用上,String类型表现为值类型语义;内存上,String类型实现为引用类型,存储在托管堆中。
4,两次创建内容相同的String对象可以指向相同的内存地址。
5,String类型被实现为密封类,不可再子类中继承。
6,String类型是跨应用程序域的,可以在不同的应用程序域中访问同一个String对象。
字符串一旦创建,就会在托管堆上分配一块连续的内存空间,我们对其的任何改变都不会影响到原String对象,而是重新创建出新的对象。
对象恒定性,为程序设计带来了极大的好处,主要包括为:
1,保证对String对象的任意操作不会改变原字符串。
2,恒定性还意味着操作字符串不会出现线程同步问题。
3,恒定性一定程度上,成就了字符串驻留。
num= Double.Parse(str);
Double.TryParse(str, out num);
num= Convert.ToDouble(str);
主要是对异常的处理机制上:如果转换失败,则Parse方法总会抛出异常,主要包括ArgumentNullException、OverflowException、FormatException等;TryParse则不会抛出任何异常,而返回false标志解析失败;Convert方法在str为null时不会抛出异常,而是返回0。
string和System.String的细微差别:
1,string为C#语言的基元类型,类似于int、char和long等其他C#基元类型,基元类型简化了语言代码,带来简便的可读性,不同高级语言对同一基元类型的标识符可能有所不同。
2,System.String是框架类库(FCL)的基本类型,string和System.String有直接的映射关系。
StringBuilder是.net提供的动态创建String对象的高效方式,以克服String对象恒定性带来的性能影响,克服了对String对象进行多次修改带来的创建大量String对象的问题。
当然在字符串连接目标较少的情况下,过度滥用StringBUilder会导致性能的浪费而非节约。
String类型的“+”连接操作,实际上是重载操作符“+”调用String.Concat来操作,而编译器则会优化这种连接操作的处理,编译器根据其传入参数的个数,一次性分配相应的内存,并依次拷入相应的字符串。
认识枚举
1.System.Enum类型是引用类型,并且是一个抽象类。
2,System.Enum类型继承自System.ValueType类型,而ValueType类型是一切值类型的根类,但是显然System.Enum并非值类型,这是ValueType唯一的特例。
一脉相承:委托、匿名方法和Lambda表达式
委托,是想了类型安全的回调方法。在.net中回调无处不在,所以委托也无处不在,事件模型建立在委托机制上,Lambda表达式本质上就是一种匿名委托。
通常一个委托被声明为:publicdelegate void CalculateDelegate(Int32 x, Int32 y); 默认返回值类型为void,
函数签名:由函数的名称和它的每一个形参的类型和种类组成。而委托可以理解为以函数作为参数的函数。
classDelegateEX
{
//声明一个委托
publicdelegate void CalculateDelegate(Int32 x,Int32 y);
//创建于委托关联的方法,二者具有相同的返回值类型和参数列表
publicstatic void Add(Int32 x,Int32 y)
{
Console.WriteLine(x+y);
}
//定义委托类型变量
privatestatic CalculateDelegate myDelegate;
publicstatic void Main()
{
myDelegate=new CalculateDelegate(Add);
//回调Add方法
myDelegate(100,200);
}
}
在类DelegateEx内部声明了一个CalculateDelegate委托类型,它具有和关联方法Add完全相同的返回值类型和参数列表,否则将导致编译时错误。将方法Add传递给CalculateDelegate构造器,也就是将方法Add指派给CalculateDelegate委托,并将该引用赋给myDelegate变量,也就表示myDeleage变量保存了指向Add方法的引用,以此实现对Add的回调
由此可见,委托表示了对其回调方法的签名,可以将方法当做参数进行传递,并根据传入的方法来动态的改变方法调用。
多播委托和委托链
在调用一个方法时,可以依次执行其绑定的所有方法,这种技术成为多播委托。
。在.NET中提供了相当简洁的语法来创建委托链,以+=和-=操作符分别进行绑定和解除绑定的操作,多个方法绑定到一个委托变量就形成一个委托链,对其调用时,将会依次调用所有绑定的回调方法
publicstatic void Main() {
myDelegate = new CalculateDelegate(Add);
myDelegate += new CalculateDelegate(Subtract);
myDelegate += new CalculateDelegate(Multiply);
myDelegate(100, 200);
}
同样以-=操作可以解除委托链上的绑定
myDelegate-= new CalculateDelegate(Add);
myDelegate(100,200);
+=和-=操作分别调用了Deleagate.Combine和Deleagate.Remove方法
委托实际上是一个类,该类继承自System.MulticastDelegate类,该类维护一个带有链接的委托列表,在调用多播委托时,将按照委托列表的委托顺序而调用的。
,首先调用CalculateDelegate的构造函数来创建一个myDelegate实例,然后通过CalculateDelegate::Invoke执行回调方法调用,可见真正执行调用的是Invoke方法。因此,你也可以通过Invoke在代码中显示调用,例如:myDelegate.Invoke(100, 200);其执行过程和隐式调用是一样的
事件是对委托的封装。
委托实现了面向对象的,类型安全的方法回调机制。
以Delegate作为委托类型的后缀,以EvenHandle作为事件委托的后缀,是规范的命名规则。
多播委托返回值一般为void,不退
直面异常
对于异常的处理,基本有两种方式来完成:一种是异常形式,一种是返回值形式
catch子句包含了异常出现时的响应代码,其执行规则是:一个try子句可以关联零个或多个catch子句,CLR按照自上而下的顺序搜索catch子句。
异常筛选器:用于表示用于可预料、可恢复的异常类,所有的异常类必须是System.Exception类型或其派生类,System.Exception类型是一切异常类型的基类,而它本身又继承自System.Object类型,用于捕获任何与CLS兼容的异常
OverflowException,算术运算、类型转换时的溢出。
OutOfMemoryException,内存不足引发的异常。
NullReferenceException,引用空引用对象时引发。
InvalidCastException,无效类型转换引发。
IndexOutOfRangeException,试图访问越界的索引而引发的异常。
ArgumentException,无效参数异常。
DivideByZeroException,被零除引发。
ArithmeticException,算术运行、类型转换等引发的异常。
FileNotFoundException,试图访问不存在的文件时引发。
Exception类提供了所有异常类型的基本属性与规则,例如:
Message属性,用于描述异常抛出原因的文本信息。
InnerException属性,用于获取导致当前异常的异常集。
StackTrack属性,提供了一个调用栈,其中记录了异常最初被抛出的位置,因此在程序调试时非常有用
.NET还提供了两个直接继承于Exception的重要子类 :ApplicationException和SystemException类
ApplicationException类型为FCL为应用程序预留的基类型,所以自定义异常可以选择ApplicationException或者直接从Exception继承;SystemException为系统异常基类,CLR自身抛出的异常继承自SystemException类型。
Serializable序列化是指将对象实例的状态存储到存储媒体的过程。在此过程中,先将对象的公共字段和私有字段以及类名称转换为字节流,然后再把字节流写入数据流。
用于序列化的构造函数,以支持跨应用程序域或远程边界的封送处理
总结自定义异常类的规则与规范,主要包括:
1,首先,选择合适的基类继承,一般情况下我们都会选择Exception类或其派生类作为自定义异常类的基类。但是异常的继承深度不宜过多,一般在2~3层是可接受的维护范围。
2,System.Exception类型提供了三个公有构造函数,在自定义类型中也应该实现三个构造函数,并且最好调用基类中相应的构造函数;如果自定义类型中有新的字段要处理,则应该为新的字段实现新的构造函数来实现。
3,所有的异常类型都是可序列化的,因此必须为自定义异常类添加SerializableAttribute特性,并实现ISerializable接口。
4,以Exception作为异常类名的后缀,是良好的编程习惯。
5,虽然异常机制提高了自定义特定异常的方法,但是大部分时候我们应该优先考虑.NET的系统异常,而不是实现自定义异常。
6,要想使自定义异常能应用于跨应用程序域,应该使异常可序列化,给异常类实现ISerializable接口是个好的选择。
异常法则
1,尽可能以逻辑流程控制来代替异常,例如非空字段的处理不要延迟到业务处理阶段,而应在代码校验时完成。对于文件操作的处理,应该首先进行路径是否存在的校验,而不是将责任一股脑推给FileNotFoundException异常来处理
2,对异常形成文档,详细描述关于异常的原因和相关信息,是减少引发异常的有效措施。
熟悉OO相关理论 WPF XAML Sliverlight 掌握VS2013、Blend ForVisual Studio、SQL SERVER这些开发工具
- 你必须知道的.net学习笔记
- 《你必须知道的.NET》第1章学习笔记
- 《你必须知道的.NET》第2章学习笔记
- 《你必须知道的.NET》第3章学习笔记
- 你必须知道的.NET》 - 学习方法论
- [你必须知道的.NET]学习方法论
- 《你必须知道的.NET》
- 你必须知道的.net
- 你必须知道的.NET
- [你必须知道的.NET]第二十回:学习方法论
- 你必须知道的.NET]第二十回:学习方法论
- jQuery学习笔记一:你必须知道的JavaScript知识
- [你必须知道的.NET] 开篇有益
- 一本你必须知道的.net
- [你必须知道的.NET]目录导航
- 推荐《你必须知道的.NET》
- [你必须知道的.NET] 开篇有益
- 《你必须知道的.NET》 - 书摘精要
- 资源大图切成小图
- swift 学习笔记
- 正向代理和反向代理
- iOS开发之-- DNS解析(网络切换的问题解决)
- 我自己改变的第二天
- 你必须知道的.net学习笔记
- C++ DLL方法(参数) 转化为C# 示例
- Google_android_JNI使用方法
- 安装android studio报错Failed to install Intel HAXM的解决方法
- 【扫盲】什么是nginx?
- 测试
- 余数相机“让照片不再过曝
- 第 11 章 CNNs(2)
- Limiting user access to your database tables