本文由中国C#技术学习中心整理 如果你对本文有不明之处请到技术论坛讨论!
4.2 引用类型
和值类型相比,引用类型不存储它们所代表的实际数据,但它们存储实际数据的引用.在C#中提供以下引用类型给你使用: ·对象类型 ·类类型 ·接口 ·代表元 ·字符串类型 ·数组
4.2.1 对象类型
对象类型是所有类型之母——它是其它类型最根本的基类.因为它是所有对象的基类,所以可把任何类型的值赋给它.例如,一个整型: object theObj = 123; 给所有的C++程序员一个警告:object并不等价于你可能正在寻找的void*.无论如何,忘掉指针总是个好主意. 当一个值类型被加框(作为一个对象利用)时,对象类型就被使用了.这一章稍后会讨论到加框和消框
4.2.2 类类型
一个类类型可以包含数据成员、函数成员和嵌套类型.数据成员是常量、字段和事件.函数成员包括方法、属性、索引、操作符、构造函数和析构函数.类和结构的功能是非常相似的,但正如前面所述,结构是值类型而类是引用类型. 和C++相比,仅允许单继承.(你不能拥有派生一个新对象的多重基类.) 但是,C#中的一个类可以派生自多重接口,该接口在下一节将得到描述.第五章 “类”专门讨论使用类编程.这一节仅打算给出C#类在哪里适合类型图的一个全貌.
4.2.3 接口
一个接口声明一个只有抽象成员的引用类型.跟C++中相似的概念为:一个结构的成员,且方法等于0.如果你不知道那些概念的任何东西,这里就是在C#中一个接口实际所做的.仅仅只存在着方法标志,但根本就没有执行代码.这就暗示了不能实例化一个接口,只能实例化一个派生自该接口的对象.
可以在一个接口中定义方法、属性和索引.所以,对比一个类,接口有什么特殊性呢?当定义一个类时,可以派生自多重接口,而你只能可以从仅有的一个类派生.
你可能会问:"OK,但我必须实现所有的接口成员,那么我能从这个途径得到什么呢?" 我想举一个来自.NET的例子:很多类实现了IDictionary 接口.你可以使用简单的类型转换访问接口: IDictionary myDict = ( IDictionary )someobjectthatsupportsit;
现在你的代码可以访问字典了.可等等,我说很多类可以实现这个接口——所以,你可以在多个地方重用代码来访问IDictionary 接口!一旦学会,任何地方都可使用.
当你决定在类设计中使用接口时,学习更多关于面向对象的设计是个好主意.这本书不能教你这些概念,但你可以学习如何创建接口.以下的代码段定义接口IFace,它只有一个方法:
interface IFace { void ShowMyFace( ); } 正如我所提到的,不能从这个定义实例化一个对象,但可以从它派生一个类.因此,该类必须实现ShowMyFace抽象方法:
class CFace:IFace { public void ShowMyFace( ) { Console.WriteLine( "implementation" ); } } 接口成员和类成员的区别在于,接口成员不能被实现.因此,我不想在下一章中再次提到这一点.
4.2.4 代表元
一个代表元封装了具有一些标志的一个方法.基本上,代表元是类型安全和函数指针的安全版本( 回调功能 ).可以同时在一个代表元实例中同时封装静态和实例方法. 尽管你可以用代表员当作具有方法,但它们的主要用途是拥有有一个类事件.再次,我想把你引到下一章,那里会详细地讨论类.
4.2.5 字符串类型
C程序员可能会诧异,但当然,C#有一个用于操作字符串数据的基本字符串类型.字符串类直接派生自对象,且它是被密封的,这意味着再不能从它派生类.就象其它类型,字符串是预定义类System String的一个别名.
它的用法十分简单: string myString = "some text"; 合并字符串同样简单: string myString = "some text" + " and a bit more"; 而如果你想访问单个字符,所要做的就是访问下标: char chFirst = myString[0]; 当比较两个字符串是否相等时,简单地使用"=="比较操作符. if ( myString == yourString ) ...
我只不过想提到,尽管字符串是一个引用类型,比较时是比较值,而不是比较引用( 内存地址 ). 字符串类型几乎用于这本书的每一个例子中,而且在这些例程中,我会介绍给你一些由字符串对象所显露的极其有趣的方法
一个数组包含有通过计算下标访问的变量.所有包含于数组中且被当作元素的变量必须是同一类型.这种类型自然被称为"数组类型".数组可以存储整数对象、字符串对象或者 你提出的任何对象.
数组的维数就是所谓的排( rank ),它决定了相关数组元素的下标数.最常用的数组是一维数组( 第一排 ).一个多维数组具有的排数大于1 .每个维的下标始于0,终于维的长度减1 .
应有足够的理论支持.让我们看一下用一个数组初始化器(array initializer )初始化的数组: string[] arrLanguages = { "C", "C++", "C#" }; 该简写效果等同以下: arrLanguages[0]="C"; arrLanguages[1]="C++"; arrLanguages[2]="C#"; 而编译器为你做了所有的工作.当然,它将同样为多维数组初始化器工作: int[,] arr = {{0,1}, {2,3}, {4,5}}; 它是以下的简写: arr[0,0] = 0; arr[0,1] = 1; arr[1,0] = 2; arr[1,1] = 3; arr[2,0] = 4; arr[2,1] = 5; 如果你不想事先初始化一个数组,但知道了它的大?蒙骶拖笳庋? int[,] myArr = new int[5,3]; 如果数组的大小必须动态地被计算,用于数组创建的语句可以象这样写: int nVar = 5; int[] arrToo = new int[nVar]; 正如我在这一节开始所陈述的,你可以往数组里面塞任何东西,只要所有的元素类型都相同.因此,如果你想把任何东西放进一个数组,就声明它的类型为对象:
4.3 加框和消框
这一章的课程中,我已经给出了各式各样的值类型和引用类型.由于速度的原因,你会使用值类型——它除了占据一定空间的内存块外,就没有什么了.但是,有时对象的方便性就象值类型一样好用.
这就是加框和消框登上了舞台的地方,加框和消框是C#类型系统的核心概念.通过允许一个值类型转换成类型对象或从类型对象转换成值类型,这种机制形成了值类型和引用类型之间的捆绑连接.任何东西终究是一个对象——但是,仅当需要它们是对象时.
4.3.1 加框转换
给一个值加框指隐式地把任何值类型转换成类型对象.当一个值类型被加框时,一个对象实例就被分配,且值类型的值被拷贝给新的对象. 看以下例子:
int nFunny = 2000; object oFunny = nFunny; 第二行的赋值暗示调用一个加框操作.nFunny整型变量的值被拷贝给oFunny对象.现在整型变量和对象变量都同时存在于栈中,但对象的值居留在堆中.
那么,它暗示着什么呢?它们的值互相独立——在它们之间没有连接.( oFunny没有引用nFunny的值. ) 以下代码说明了结果:
int nFunny = 2000; object oFunny = nFunny; oFunny = 2001; Console.WriteLine( "{0} {1}", nFunny, oFunny ); 当代码改变oFunny的值时,nFunny的值并没有改变.只要你脑袋中有这个copy动作,就能够使用值类型的对象功能,发挥出你的巨大优势!
4.3.2 消框转换
和加框相比,消框是显式操作——必须告诉编译器,你想从对象中抽取出哪一种值类型.当执行消框操作时,C#检测所请求的值类型实际上存储在对象实例中.经过成功的校验,该值被消框.
这就是消框如何执行:
int nFunny = 2000; object oFunny = nFunny; int nNotSoFunny = ( int )oFunny; 如果错误地请求一个double值 double nNotSoFunny = ( double )oFunny; 通用语言运行时( Common Language Runtime,简写CLR )将会引发一个InvalidCastException异常.你可以在第7章 "异常处理" 中学到更多有关异常处理的知识.
4.4 小结
在这一章中,你学到了C#中用到的各种类型.简单的值类型包括整型、布尔型、浮点型和小数型.你会非?5赜玫揭恍├嘈?进行数学和金融的计算,还有逻辑表达.
在介绍引用类型之前,我显示了一个看起来象类的结构类型.它几乎如一个类般地运作,但它只是一个值类型,这使它更加适合需要有大量的小对象的场合.
引用类型起始于所有对象之母的objedt本身.object是C#中所有对象的基类,且它同样用于值类型的加框和消框.除此之外,我还让你领略了代表元、字符串和数组.
令C#程序员十分神气的类型就是类.它是C#面向对象编程的心脏,下一章整章专门让你迅速理解这个激动人心且功能强大的类型
本文由中国C#技术学习中心整理 如果你对本文有不明之处请到技术论坛讨论!
|