falla.zhang

导航

C#与.NET 3.0高级程序设计读书笔记

转自网络

第三章:
一:main带的参数可以直接访问args数组,也可以通过Environment类的静态函数GetCommandLineArgs获得。Environment类还包含其他有关应用程序和操作系统的有关信息。
二:类必须在new之后才能用,c#不会将类类型分配到栈上
三:之用定义构造函数,无需定义析构函数,因为c#有垃圾自动收集机制
四:有关“分工:(separation of concerns)。类应该只定义自己相关的功能,调用自己的最好放到另外一个类中。例如HelloClass类和HelloApp类。
五:Console类包含一些控制台相关的设置。输出可以对字符格式化,格式方式见详细资料。也可以用String.Format()总使用格式化字符。
六:成员可见性包括:public,private,protected,internal,protected internal。
七:类型可见性包括:public,internal
八:类成员有默认值,局部变量没有。局部变量要求强制赋值,但是用作输出参数时例外。
九:类成员可在定义时赋值。这在c++中是不可以的。
十:有关常量数据。与C++中不同,C#中const关键字不能用来限定参数或返回值,只能保留用来创建数据或实例一级的数据。所赋的值必须是编译时知道的,被硬编码到程序集中。值得注意的是所有的常量数据隐含为静态的,因此在类型外部引用时要加上它所属的类型名。
十一:只读字段。与const字段不同,只读字段允许定义在编译时不知道的值,同样是赋值后即不能被改变。可以在定义是赋值,也可以放到构造函数中赋值。只读字段不是默认为静态的。
十二:有关static
    1、静态方法。只能用“类名.静态方法名”的方式调用。
    2、静态数据。只分配一次,在所有对象实例之间共享,每一个实例都维护着一份副本。(在内存中占几个空间?)。非静态方法也可以访问修改静态数据。
    3、静态构造函数。用来对静态数据初始化。几个特性:一个给定的类(结构)只能定义一个静态构造函数;静态构造函数仅执行一次,与创建了多少这种类型的对象无关;静态构造函数不带访问修饰符,也不带任何参数;当静态构造函数创建这个类的实例时,或者在调用者访问第一个静态成员之前,运行库会调用静态构造函数;静态构造函数在任何实例级别的构造函数之前执行。
    4、静态类。C#2.0新增。只能包含静态方法和静态数据。不能用new创建。c#2.0之前,防止创建这类数据的方法一是重定义默认构造函数为私有或用abstract将类标记为抽象类型,但是不是类型安全的。
十三:有关参数修饰符
    1、无修饰符。默认按值传递。
    2、out。按引用传递,传递前不需要初始化。
    3、params。传递一组可变个数的相同类型参数,方法只能有一个params修饰符,且必须为最后一个。
    4、ref。按引用传递。传递前必须初始化。
十四:c#有四种循环。for;foreach/in:while;do/while循环。
十五:判断结构和关系运算符
    1、if/else只可以操作boolean表达式。
    2、siwth语句。每一个分支以break或goto结尾。
十六:值类型和引用类型
    1、值类型都分配在栈上,一离开定义的作用域,就会从内存删除。都继承自System.ValueTpe
    2、C#中,枚举和结构都是值类型。结构类型也是用new创建,但是是在栈上分配。也可以不用new,但是要对每一个字段赋值。
    3、结构的默认构造函数是保留的,不能重定义。结构不能被继承。
    4、包含引用类型的值类型。结构里面包含类时。赋值将会有两个独立的结构,但是每个里面都包含指向同一个对象的引用。这是“浅复制”。想实现“深复制”要实现ICloneable接口。
    5、按值传递引用类型。传递的复制的一份指向调用者对象的引用。因此可以改变对象的域数据,但是重新赋值对调用者不可见。
    6、按引用传递引用类型。完全是对同一个对象操作,可以重新赋值。
十七:装箱和拆箱
值类型转换成引用类型是“装箱”反过程成为“拆箱”
十八:枚举。总是用带有前缀的方式来引用。
十九:Object类
1、包含一些实例级别和类级别的成员。某些实例级别的带有virtual关键字,可以被派生类重写。
2、实例级:Equals,GetHashCode,GetType,ToString,Finalize,MeberwiseClone
类级:Equals,ReferenceEquals
二十:系统数值类型
二十一:String
    string是引用类型,但是相等行运算符比较的是对象的值而不是所引用的内存。注意“逐字字符串”的用法。
    C#的字符串是不可变的,因此引入StringBuilder类,文本密集的应用程序应使用StringBuilder。
二十二:数组类型
是引用类型,继承自System.Array。如果声明时知道数组的值,可以在大括号中指定,同时new可以省略。系统会设定默认值。
数组分为矩阵数组和交错数组。
数组清空只是把值设定成默认值。
二十三:可空类型
可供类型即是可以赋予null的类型。使用类似int? num=0;的方式,只能针对值类型。局部可空变量必须赋予一个初始值。使用??可以给一个获得的实际是null时给一个可空类型赋值。例如:int? myData=ds.GetNum()??100;
二十四:命名空间

C#笔记第四章“C#2.0面向对象编程”
一:C#类类型
    1、如果要用过new someClass()来创建新对象,必须重定义默认构造函数
    2、仅仅返回类型不同不能重载
    3、this关键字可以用来转发构造函数调用
二、oop三大支柱:封装,继承,多态。继承有is-a和has-a的关系,多态靠虚拟和抽象来支持。
三:C#封装支持。
    1:传统访问方法和修改方法
    2:类属性,其中value代表赋值号邮编的数据,数据类型取决于代表的实际种类。
       value不是关键字。而是“上下文关键字”。
对属性可以使用C#的内建运算符(++)。
属性在内部实际转换成set_XXX和get_XXX,因此自己不能再定义这种的函数了。
只读只写属性,属性的get、set也可以设定可见性级别。
存在“静态属性”
四:C#的继承支持
    1:is-a模型
        a:每个子类应该显示调用一个合适的积累构造函数,可以使用关键字base。
        b:C#不允许多继承的出现。
        c:使用sealed关键字来密封类,不让它被继承。
    2:has-a模型(包含/委托)
    3:嵌套类型
公共嵌套类型可以被任何人调用,私有嵌套类型只能被包含类的成员使用。嵌套类一般用来对外部类起辅助作用,而不是为了给外部世界使用。在外部使用时要使用包含类型的作用域来限定它。
五:C#的多态支持
    1:virtual和override关键字
如果基类想定义一个可以被子类重写的方法,它必须指定方法是虚的,可以定义该方法的一个默认实现。
    2:sealed关键字也可以用到类型成员上,以防止虚成员将类被继承类型所重写。起到只密封一些方法或属性的作用。(用在非虚方法上应该没有意义,或者错误?)
    3:抽象类。
        a:不想某个类被创建实例的时候用。使用abstract关键字。
        b:强制多态。基类定义抽象方法,子类必须实现。抽象方法不包含默认的实现。
        c:抽象方法只能用在抽象类中。子类如果不实现抽象方法,也必须声明用abstract修饰。
    4:成员隐藏。如果一个派生类重定义了一个继承自基类的相同的成员,派生类就隐藏了父成员。这时编译器会报警,解决办法一是采用virtual和override,另一种是在子类类型前加上new,明确指定就是要隐藏父类的版本。可以隐藏的包括任何基类成员类型。但是使用强制类型转换仍然可以调用一个已隐藏的基类实现。
六:C#的类型转换规则
    1:当两个类之间是is-a关系时,将派生类型存储在基类引用中总是安全的。这是“隐式类型转换”。小类型到大类型。
    2:“显示类型转换”,大类型到小类型,使用前最好先用is判断下。
七:分部类型
使用关键字partial。
八:XML注释
这是一个大问题。GhostDoc可以自动生成部分xml注释,NDoc可以提取XML注释成帮助文档。

第五章(对象的生命周期)
一:类、对象和引用
类是描述类型的实例在内存中什么样子的,对象是在托管堆中创建的类的实例,new返回的指向对象的引用,这个引用是存放在栈里的。
二:对象生命周期的基础
    1:法则:使用new关键字将一个对象分配在托管堆上,然后就不用再管。当一个对象从代码库的任何部分都不可达是,垃圾回收器会删除它。但不一定是在函数返回后立即回收。
    2:CIL的new指令
在IL代码中是newobj,它执行时步骤如下:计算分配对象所需要的总内存数,如果托管堆中有足够空间,就调用构造函数分配,它的地址恰好是下一个对象指针的上一个位置,在将引用返回之前,移动下一个对象的指针。如果不够则进行垃圾回收。
法则:如果托管堆没有足够的内存分配所请求的对象,就会进行垃圾回收。
垃圾回收时会挂起当前进程的所有线程,以保证他们在回收时不会访问堆。
三:应用程序根的作用
根就是一个存储位置,其中保存着对堆上一个对象的引用。垃圾回收时,没有根的对象认为是不可达的,CLR对建立“对象图”来判断。
实际上,垃圾回收器使用了两个不同堆,一个专门用来存储非常大的对象,这个堆在回收周期中很少顾及。
四:对象的代
    1:一般分为三代,从0开始。分代的目的基于以下看法:对象在堆中存在的时间越长,它越应该被保留。一般先从第0代进行垃圾收集,需要时再向上代收集。
    2:System.GC。强制回收一般发生在:应用程序将要进入一段代码,后者不希望被可能的垃圾回收中断;应用程序刚刚分配非常多的对象,想尽可能的删除已请求的内存。
调用Collect()后应该总是调用WaitForPendingFinalizers()。Collect()可以带参数,表示第几代。即使显式垃圾回收一次,CLR在幕后也执行了多次。
五:构建可终结对象
    1:System.Object定义的Finalize()虚方法默认什么都不做。重写的Finalize()是由垃圾回收器调用的,或者当APPDomain从内存卸载时被调用。
重写Finalize()的唯一原因是:C#类使用了PInvoke或复杂的COM互操作性人物使用了非托管的资源。
注意不能在结构类型上重写Finalize(),因为它是值类型。
    2:重写Finalize()
不能使用override关键字,而应该使用类似析构函数的方式。因为C#编译器会在il代码中自动加上try语句,try语句中方的是你自定义的finalize要执行的操作,而finally语句里是它自动生成的一些错误检测代码,里面有执行基类的Finalize()。如果要用override关键字,就有了两个Finalize()函数。
    3:终结过程的细节
一般只有在使用了非托管实体的类型时才重定义Finalize(),托管的自己会终结,没有意义再定义,并且终结时要花费时间的。
在托管堆分配对象时,如果有自定义的Finalize方法,对象被标记为可终结的,同时一个指向该对象的指针被保存在“终结队列”里,“终结队列”是一个垃圾回收器维护的表,指向每一个在从堆上删除之前必须被终结的对象。当垃圾回收器确定到了从内存中释放一个对象的时间时,它检查终结队列上的每一项,并将对象从堆上复制到另一个“终结可达表”的托管结构上,下一个垃圾回收时产生另一个线程,为每一个可达表中的对象调用Finalize方法。因此,终结一个对象,至少要进行两次垃圾回收。
六:构建可处置对象
    1:结构和类类型都支持IDispoable,而Fianlize只适用于类类型。
    2:Dispose必须要被手工调用。Dispose()方法不但负责释放一个对象的非托管资源,还应该对任何它包含的可处置对象调用Dispose()。
    3:法则:如果对象支持IDispoable,总是要对直接创建的对象调用Dispose()。
    4:重用C#的using关键字,可以减少使用try,finally语句块。
七:构建可终结类型和可处置类型
    1:Dispose如果处理了非托管资源,应该调用GC.SuppressFinalize方法,通知CLR不再调用析构函数。
    2:微软有一个可处置模式的模板,可以让Finalize()和Dispose()和平相处。

posted on 2010-02-01 16:45  falla.zhang  阅读(373)  评论(0编辑  收藏  举报