版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、知识点 1:引用和值类型总结从概念上看, 值类型直接存储其值, 而引用类型存储对其值的引用。 这两种类型存储在内存的不同地方。在C# 中,我们必须在设计类型的时候就决定类型实例的行为。这种决定非常重要,用 CLR via C# 作者 Jeffrey Richter 的话来 说, “不理解引用类型和值类型区别的程序员将会给代码引入诡异的 bug 和性能问题( I believe that a developer who misunderstands the difference between reference types and value types will introduce subt
2、le bugs and performance issues into their code.) ” 。这就要求我们正确理解和使用值类型和引用类型。1. 通用类型系统C# 中,变量是值还是引用仅取决于其数据类型。C# 的基本数据类型都以平台无关的方式来定义。C# 的预定义类型并没有内置于语言中,而是内置于 .NET Framework 中。 .NET 使用通用类型系统(CTS) 定义了可以在中间语言 ( IL)中使用的预定义数据类型,所有面向 .NET 的语言都最终被编译为IL ,即编译为基于CTS类型的代码。例如,在 C# 中声明一个int 变量时,声明的实际上是CTS 中 System.I
3、nt32 的一个实例。这具有重要的意义:确保 IL 上的强制类型安全;实现了不同 .NET 语言的互操作性;所有的数据类型都是对象。它们可以有方法,属性,等。例如:int i;i = 1;string s;s = i.T oString();MSDN 的这张图说明了 CTS 中各个类型是如何相关的。注意,类型的实例可以只是值类型或自描述类型,即使这些类型有子类别也是如此。2. 值类型C# 的所有值类型均隐式派生自 System.ValueType :结构体: struct (直接派生于 System.ValueType ) ;数值类型:整 型: sbyte ( System.SByte 的别名
4、), short ( System.Int16 ) , int ( System.Int32 ) , long(System.Int64 ) , byte (System.Byte ) , ushort ( System.UInt16 ) , uint ( System.UInt32 ) ,ulong ( System.UInt64 ) , char ( System.Char ) ;浮点型: float ( System.Single ) , double ( System.Double ) ;用于财务计算的高精度decimal 型: decimal ( System.Decimal ) 。
5、bool 型: bool ( System.Boolean 的别名) ;用户定义的结构体(派生于System.ValueType ) 。枚举: enum (派生于 System.Enum ) ;可空类型(派生于System.Nullable<T>泛型结构体,T?实际上是 System.Nullable<T>的别名) 。每种值类型均有一个隐式的默认构造函数来初始化该类型的默认值。例如:int i = new int();等价于:Int32 i = new Int32();等价于:int i = 0;等价于:Int32 i = 0;使用 new 运算符时,将调用特定类型的默
6、认构造函数并对变量赋以默认值。在上例中,默认构造函数将值0 赋给了 i 。 MSDN 上有完整的默认值表。关于 int 和 Int32 的细节,在我的另一篇文章中有详细解释: 理解 C# 中的 System.Int32和 int 。所有的值类型都是密封( seal )的,所以无法派生出新的值类型。值得注意的是, System.ValueType 直接派生于 System.Object 。 即 System.ValueType 本身是一个类类型,而不是值类型。其关键在于ValueType 重写了 Equals() 方法,从而对值类型按照实例的值来比较,而不是引用地址来比较。可以用Type.IsV
7、alueType 属性来判断一个类型是否为值类型:复制代码 代码如下 :TestType testType = new TestType ();if (testTypetype.GetType().IsValueType)Console.WriteLine("0 is value type.", testType.T oString();3. 引用类型C# 有以下一些引用类型:数组(派生于System.Array ) 用户用定义的以下类型:类: class (派生于 System.Object ) ;接口: interface (接口不是一个“东西” ,所以不存在派生于何处
8、的问题。 Anders 在 C#Programming Language 中说,接口只是表示一种约定 contract ) ;委托: delegate (派生于 System.Delegate ) 。object( System.Object 的别名);字符串: string ( System.String的别名)。可以看出:引用类型与值类型相同的是,结构体也可以实现接口;引用类型可以派生出新的类型,而值类型不能;引用类型可以包含 null 值,值类型不能(可空类型功能允许将null 赋给值类型) ;引用类型变量的赋值只复制对对象的引用, 而不复制对象本身。 而将一个值类型变量赋给另一个值类型
9、变量时,将复制包含的值。对于最后一条,经常混淆的是string 。我曾经在一本书的一个早期版本上看到 String 变量比 string 变量效率高;我还经常听说 String 是引用类型, string 是值类型,等等。例如:string s1 = "Hello, "string s2 = "world!"string s3 = s1 + s2;/s3 is "Hello, world!"这确实看起来像一个值类型的赋值。再如:string s1 = "a"string s2 = s1;s1 = "b&q
10、uot;/s2 is still "a"改变 s1 的值对 s2 没有影响。这更使string 看起来像值类型。实际上,这是运算符重载的结果,当 s1 被改变时, .NET 在托管堆上为 s1 重新分配了内存。这样的目的,是为了将做为引用类型的 string 实现为通常语义下的字符串。4. 总结C# 中,变量是值还是引用仅取决于其数据类型。C# 的值类型包括:结构体(数值类型, bool 型,用户定义的结构体) ,枚举,可空类型。C# 的引用类型包括:数组,用户定义的类、接口、委托, object ,字符串。数组的元素,不管是引用类型还是值类型,都存储在托管堆上。引用类型在
11、栈中存储一个引用, 其实际的存储位置位于托管堆。 为了方便, 本文简称引用类型部署在托管推上。值类型总是分配在它声明的地方:作为字段时,跟随其所属的变量(实例)存储;作为局部变量时,存储在栈上。值类型在内存管理方面具有更好的效率, 并且不支持多态, 适合用作存储数据的载体; 引用类型支持多态,适合用于定义应用程序的行为。应该尽可能地将值类型实现为具有常量性和原子性的类型。应该尽可能地确保0 为值类型的有效状态。应该尽可能地减少装箱和拆箱。知识点 2 :封装可以把程序按某种规则分成很多 “块 “,块与块之间可能会有联系, 每个块都有一个可变部分和一个稳定的部分。 我们需要把可变的部分和稳定的部分
12、分离出来, 将稳定的部分暴露给其他块,而将可变的部分隐藏起来,以便于随时可以让它修改。这项工作就是封装。例如: 在用类实现某个逻辑的时候, 类就是以上所说的块, 实现功能的具体代码就是可变的部分,而 public 的方法或者属性则是稳定的部分。封装的意义封装的意义在于保护或者防止代码 (数据) 被我们无意中破坏。 在面向对象程序设计中数据被看作是一个中心的原素并且和使用它的函数结合的很密切, 从而保护它不被其它的函数意外的修改。封装提供了一个有效的途径来保护数据不被意外的破坏。 相比我们将数据 (用域来实现) 在程序中定义为公用的( public )我们将它们(fields) 定义为私有的(
13、private )在很多方面会更好。私有的数据可以用两种方式来间接的控制。下面我们看一些c# 例子来学习这两种方法用以封装数据。 第一种方法, 我们使用传统的存、 取方法。 第二种方法我们用属性( property ) 。无论我们使用哪种的方法, 我们的目标是在使用数据的同时不能使它受到任何的破坏和改变。有如下好处1 、使用者只需要了解如何通过类的接口使用类,而不用关心类的内部数据结构和数据组织方法。2 、高内聚,低耦合一直是我们所追求的,用好封装恰恰可以减少耦合3 、只要对外接口不改变,可以任意修改内部实现,这个可以很好的应对变化4 、类具有了简洁清晰的对外接口,降低了使用者的学习过程用传统
14、的读、写方法封装让 我们来看一 个例子有 一个类 Grade ( 年级 信息类 ) ,为 了操 纵这个类 中 的数据( stringgradeName (年级名称) )我们定义了一个读方法和一个写方法。using system;public class Gradeprivate string gradeName;/ 读方法public string GetGradeName()return gradeName;/ 写方法public void SetGradeName(string gradeName)this.gradeName=gradeName;通过上面的方法, 我们可以保护私有数据不被
15、外部程序所破坏。 现在我们使用两个不同的方法来写和读数据public staticvoid Main(string args)Grade grade = new Grade();grade.SetGradeName("S2")Console.WriteLine("TheGrade is :"+grade.GetGradeName();在 上 面 的 例 子 中 , 我 们 不 能直接 访 问 类 Grade 的 实 例 grade 中 的 私 有数 据 (stringgradeName) ,我们只能通过这两个方法来访问。用属性来实现封装属性是 c# 引入
16、的一种语言成分,只有很少的语言支持属性。通过对属性的读和写来保护类中的域。第一种方法体身也是一种好的方式,但用属性来实现封装会更方便。现在我们来看一个例子:using system;public class Gradeprivate string gradeName;public string GradeNamegetreturn gradeName;setgradeName =value;public static void Main(string args)Grade grade= newGrade ();grade.GradeName="S2"Console.Writ
17、eLine("TheGrade is :0",grade.GradeName);通过上面的例子,我们可以看到如何通过属性来实现封装。属性具有两种操作get 和 set 。get 用来返回属性域的值。 set 通过 value 这个变量来给属性域赋值。属性可以设为只读的(read-only) 。这只需属性只具有一个set 操作。只读属性public string GradeNamegetreturn gradeName;在上 面的例子中 我们看到了 如何来实现一个只读的 属性。 类 Grade 拥有 一个GradeName 属性只实现了 get 操作。它省略了写操作。这个特别
18、的类拥有一个构造器,用来接受一个字符串变量。在Main 方法创建了一个新的对象grade 。对象 grade 的实例使用了类 Grade 带有一个字符串参数的构造器。因为上面的属性是只读的,所以我们不给域 gradeName 赋值并且我们只侧读取此域中的值。 当然属性也可以是只写的 ( write-only ) ,这只需属性只具有一个get 操作。public string GradeNamesetthis.gradeName = value;在上 面的例子中 我们看到了 如何来实现一个只写的 属性。 类 Grade 拥有 一个GradeName 属性只实现了 set 操作。它省略了读操作。总
19、结封装是朝着面向对象程序设计迈出的第一步。 本文主要展示了一些封装的知识。 用传统的读、 写两种方法可以实现封装, 另一种实现封装的方法是使用属性。 使用属性的好处在于对象的使用者可以用一条语句来操作内部的数据知识点 3 :继承1、 继承的定义二、为了提高软件模块的可复用性和可扩充性, 以便提高软件的开发效率, 我们总是希望能够利用前人或自己以前的开发成果, 同时又希望在自己的开发过程中能够有足够的灵活性,不拘泥于复用的模块。 C# 这种完全面向对象的程序设计语言提供了两个重要的特性-继承性 inheritance 和多态性 polymorphism 。继承是面向对象程序设计的主要特征之一,
20、它可以让您重用代码, 可以节省程序设计的时间。 继承就是在类之间建立一种相交关系, 使得新定义的派生类的实例可以继承已有的基类的特征和能力,而且可以加入新的特性或者是修改已有的特性建立起类的新层次。现实世界中的许多实体之间不是相互孤立的, 它们往往具有共同的特征也存在内在的差别。人们可以采用层次结构来描述这些实体之间的相似之处和不同之处。为了用软件语言对现实世界中的层次结构进行模型化, 面向对象的程序设计技术引入了继承的概念。 一个类从另一个类派生出来时, 派生类从基类那里继承特性。 派生类也可以作为其它类的基类。从一个基类派生出来的多层类形成了类的层次结构。注意: C# 中,派生类只能从一个
21、类中继承。这是因为,在C+ 中,人们在大多数情况下不需要一个从多个类中派生的类。从多个基类中派生一个类这往往会带来许多问题,从而抵消了这种灵活性带来的优势。C# 中,派生类从它的直接基类中继承成员:方法、域、属性、事件、索引指示器。除了构造函数和析构函数,派生类隐式地继承了直接基类的所有成员2、 C# 中的继承符合下列规则:1 、 继承是可传递的。 如果 C 从 B 中派生, B 又从 A 中派生, 那么 C 不仅继承了 B中声明的成员,同样也继承了 A 中的成员。 Object 类作为所有类的基类。2 、派生类应当是对基类的扩展。派生类可以添加新的成员,但不能除去已经继承的成员的定义。3 、
22、构造函数和析构函数不能被继承。除此以外的其它成员,不论对它们定义了怎样的访问方式,都能被继承。基类中成员的访问方式只能决定派生类能否访问它们。4 、 派生类如果定义了与继承而来的成员同名的新成员, 就可以覆盖已继承的成员。但这并不因为这派生类删除了这些成员,只是不能再访问这些成员。5 、 类可以定义虚方法、 虚属性以及虚索引指示器, 它的派生类能够重载这些成员,从而实现类可以展示出多态性。6 、派生类只能从一个类中继承,可以通过接吕实现多重继承。下面的代码是一个子类继承父类的例子:3、 . 访问与隐藏基类成员(1) 访问基类成员通过 base 关键字访问基类的成员:调用基类上已被其他方法重写的
23、方法。指定创建派生类实例时应调用的基类构造函数。基类访问只能在构造函数、实例方法或实例属性访问器中进行。从静态方法中使用 base 关键字是错误的。(2) 隐藏基类成员想想看, 如果所有的类都可以被继承, 继承的滥用会带来什么后果?类的层次结构体系将变得十分庞, 大类之间的关系杂乱无章, 对类的理解和使用都会变得十分困难。 有时候,我们并不希望自己编写的类被继承。另一些时候,有的类已经没有再被继承的必要。C#提出了一个密封类(sealed class )的概念,帮助开发人员来解决这一问题。密封类在声明中使用 sealed 修饰符, 这样就可以防止该类被其它类继承。 如果试图将一个密封类作为其它
24、类的基类, C# 将提示出错。理所当然,密封类不能同时又是抽象类,因为抽象总是希望被继承的。在哪些场合下使用密封类呢?密封类可以阻止其它程序员在无意中继承该类。 而且密封类可以起到运行时优化的效果。 实际上, 密封类中不可能有派生类。 如果密封类实例中存在虚成员函数,该成员函数可以转化为非虚的,函数修饰符virtual 不再生效。(3) 密封方法我们已经知道,使用密封类可以防止对类的继承。C#还提出了密封方法( sealedmethod ) 的概念,以防止在方法所在类的派生类中对该方法的重载。对方法可以使用 sealed 修饰符,这时我们称该方法是一个密封方法。不是类的每个成员方法都可以作为密
25、封方法密封方法, 必须对基类的虚方法进行重载,提供具体的实现方法。所以,在方法的声明中, sealed 修饰符总是和override 修饰符同时使用。(4) 使用 new 修饰符隐藏基类成员使用 new 修饰符可以显式隐藏从基类继承的成员。 若要隐藏继承的成员, 请使用相同名称在派生类中声明该成员,并用 new 修饰符修饰它。通过继承隐藏名称采用下列形式之一:a、引入类或结构中的常数、指定、属性或类型隐藏具有相同名称的所有基类成员。b 、引入类或结构中的方法隐藏基类中具有相同名称的属性、字段和类型。同时也隐藏具有相同签名的所有基类方法。c、引入类或结构中的索引器将隐藏具有相同名称的所有基类索引
26、器。注意:在同一成员上同时使用 new 和 override 是错误的。同时使用 new 和virtual 可保证一个新的专用化点。在不隐藏继承成员的声明中使用 new 修饰符将发出警告。四、多级继承一些面向对象语言允许一个类从多个基类中继承, 而另一些面向对象语言只允许从一个类继承,但可以随意从几个接口或纯抽象类中继承。只有 C+ 支持多级继承,许多程序员对此褒贬不一。多级继承常会引起继承来的类之间的混乱, 继承而来的方法往往没有唯一性, 所以 C# 中类的继承只可以是一个,即子类只能派生于一个父类, 而有时你必须继承多个类的特性, 为了实现多重继承必须使用接口技术。五、继承与访问修饰符访问
27、修饰符是一些关键字, 用于指定声明的成员或类型的可访问性。 类的继承中有四个访问修饰符: public protected internal private 。使用这些访问修饰符可指定下列五个可访问性级别: public protected internal internal protected private。声明的可访问性意义public访问不受限制。protected访问仅限于包含类或从包含类派生的类型。internal访问仅限于当前项目。protected internal访问仅限于从包含类派生的当前项目或类型。private访问仅限于包含类型。1、继承中关于可访问域的一些问题基类的所
28、有成员(实例构造函数、析构函数和静态构造函数除外)都由派生类型继承。这甚至包括基类的私有成员。但是,私有成员的可访问域只包括声明该成员的类型的程序文本。2、继承中关于属性的一些问题和类的成员方法一样, 我们也可以定义属性的重载、虚属性、抽象属性以及密封属性的概念。与类和方法一样,属性的修饰也应符合下列规则:属性的重载1 .在派生类中使用修饰符的属性,表示对基类中的同名属性进行重载。2 .在重载的声明中,属性的名称、类型、访问修饰符都应该与基类中被继承的属 性一致。3 .如果基类的属性只有一个属性访问器,重载后的属性也应只有一个。但如果基类的属性同时包含 get和set属性访问器,重载后的属性可以只有一个,也可以同时有两 个属性访问器。而只是注意: 与方法重载不同的是, 属性的重载声明实际上并没有声明新的属性,为已有的虚属性提供访问器的具体实现。虚属性1. 使用 virtual 修饰符声明的属性为虚属性。2. 虚属性的访问器包括get 访问器和 set 访问器,同样也是虚的。抽象属性1. 使用 abstract 修饰符声明的属性为抽象属性。2. 抽
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年县乡教师选调考试《教育学》练习题库附答案详解ab卷
- 2026年预售证合同(1篇)
- 安徽省安庆市2026年重点学校初一新生入学分班考试试题及答案
- 高考全国II卷数学试题
- 上海市重点学校小升初入学分班考试英语考试试题及答案
- 陕西省初一入学语文分班考试真题含答案
- 2026执业医师加试之军事医学考试真题试卷+参考答案
- 2026年重庆市中考语文试题(附答案)
- 2025~2026学年河南省平顶山市汝州市第三高级中学度高三上学期生物期末试卷
- 2025~2026学年河北省石家庄市七县联合体联考高一上学期1月月考生物试卷
- 住建领域有限空间作业安全管理知识课件
- 企业一般工业固体废物管理制度(打印公示上墙参考版)A3打印
- 《中华民族大团结》(初中)-第5课-共同保卫伟大祖国-教案
- 车间划线及颜色标准
- 济南版七年级生物下册期中试卷(含答案)
- DB33-T 2360-2021 彩色森林营建技术规程
- 急慢性肾小球肾炎病人的护理课件
- 17G911 钢结构施工安全防护
- 招标控制价编制实例
- 骨关节炎药物治疗进展
- ISO-TS16949:质量管理体系中英文对照版
评论
0/150
提交评论