C#入门经典第三版读书笔记全2.docx
在类的构造函数中出现表示对正在构造的对象本身的引用,在类的方法中出现 表示对调用该方法的对象的引用,在结构的构造上函数中出现表示对正在构造 的结构的引用,在结构的方法中出现表示对调用该方法的结果的引用在C#系统中,this实际上是个常量,所以不能使用this+这样的运算this保留字一般用于限定同名的隐蔵成员、将对象本身做为参数、声明索引访 问器、判断传入参数的对象是否为本身1 «类和结构的区别?答:类:类是引用类型在堆上分配,类的实例进行赋值只是复制了引用,都指向同一段 实际对象分配的内存;类有构造和析构函数;类可以继承和被继承。结构:结构是值类型在栈上領(虽然栈的访问速度比较堆要快,但栈的资源有限放), 结构的赋值将分配产生一个新的对象。结构没有构造函数,但可以添加。结构没有析构函数。结构不可以继承自另一个结构或被继承,但和类样可以继承自接口。2 .抽象类和接口的区别?答:抽象类(abstract class)可以包含功能定义和实现,接口(interface)只能 包含功能定义抽象类是从系列相关对象中抽象出来的概念,因此反映的是事物的内部共 性;接口是为了满足外部调用而定义的个功能约定,因此反映的是事物的外 部特性分析对象,提炼内部共性形成抽象类,用以表示对象本质,即“是什么”为外部提供调用或功能需要扩充时优先使用接口。接一般只有方法,而没有 数据成员或属性。抽象类有方法,也有数据成员或属性,一般情况下,优先考虑 用接口,只有当可能要访问到数据成员或属性时,用抽象类。3 .概述深度复制和浅度复制深度复制就是将引用成员指向的对象也进行复制。实际的过程是创建新的引用成 员指向的对象,然后复制对象包含的数据。浅度复制的时候,对于值类型成员,会复制其本身(值类型变量本身包含了所有 数据,复制时进行按位拷贝);对于引用类型成员(注意它会引用另个对象), 仅仅复制引用,而不创建其引用的对象。结果就是:新对象的引用成员和复制 对象的引用成员指向了同一个对象第十章定义类成员本章内容1 .定义类成员2 .类成员的其他议题3 .接的实现4 .部分类知识点1 .定义字段字段(一般首字母小写,或下戈I践开头):在类中直接定义的变量。变量一定 要先声明,再赋值,才能使用.>命名规则:1)必须以“字母” 或符号开头.不要以数字开头2)后面可以跟任意“字母”、数字、下划线.定义变量时,变量名要有意义A注意:1)你起的变量名不要与c#系统中的关键字重复.2)在c#中,大小写是敏感的> 字段用标准的变量声明格式和前面介绍的修饰符来声明(也可以初始化),例 如:class myClass(public int Mylnt;)公共字段推荐用PascalCasing的形式来命名,私有字段没有推荐的命名形式, 一般使用came ICasing的形式来命名。字段可以使用关键字readonly,表示这个字段只能在执行构造函数的过程中赋 值,或由初始化语句赋值,例如:class myClasspublic readonly int Mylnt=17;)字段也可以使用static声明为静态的:class myClass(public static int Mylnt;)2 .定义属性属性(一般首字母大写):定义一个public变量,这个变量里含有get, set两个 方法(属性不是变量,其实质是函数)0 get读取属性,return返回读取值,set写 属性,关键字value存储用户赋的值。字段与属性的区别就是可以进行非法值的判断。 允许外部访问的变量一定要声明为属性。它是代表类的实例或类中的个数据项的 成员。像字段:它是命名的类成员、有类型、可以被赋值和读取;不像字段:它是函数成员、不为数据存储分配内存、它执行代码。属性可以使用vartual、override和abstract关键字,但是不能将这几个关键 字用于字段。语法:访问修饰符类型名属性名 get return 字段;set(私有字段=value J )功能:允许外部访问的变量必须设为属性,可以防止用户设非法值,用来保护 私有字段赋值的正确性。get块一般有一个属性类型的返回值,简单的属性一般与一个私有字段相关 联,用来控制对这个字段的访问,这是get块可以直接返回该字段的值;上面的字 段外部不能访问,只能通过属性来访问该字段。set函数以类似的方式把个值赋给字段,这里可以使用关键字value引用用户 提供的属性值;value值等于类型和属性相同的一个值,如果属性的类型和字段的类 型相同,就不用进行类型转换。静态属性:不能访问类的实例成员,虽然它们能被实例成员访问;它总是存在,不管类是否有实例;当从类的外部访问时,必须使用类名引用,而不是实例名;其设计目的主要是为了实现面向对象()中的封装思想。根据该思想,字段 最好设为private, 一个精巧的类最好不要直接把字段设为公有提供给客户调用 端直接访问。3 .定义方法方法使用标准的函数格式,以及可访问性,和可选的static修饰符来声明,例 如:class myClasspublic string MyStringOreturn "这是一个方法”;)解析:class myClass(开始声明方法代码public string MyStringO(return "这是一个方法”;)结束声明方法代码)公共方法也用PascalCasing的形式来命名。> 注意:如果声明了static关键字,这个方法就只能通过类来访问,而不能通过 对象实例还访问。>也可以在方法定义中使用下面的关键字:virtual方法可以重写;abstract方法必须在非抽象的派生类中重写(只用于抽象类) override一一一方法重写了一个基类方法(要重写方法,必须用这个关键字)extern方法定义在其他地方;构造方法是种创建对象的特殊方法,方法名和类名样,没有返回值,连void 都不用,就是为了初始化类的成员。构造方法不能被。verride。构造函数可以有参数,new对象的时候传递函数参数即可。如果不指定构造函 数,则类有一个默认的无参构造函数。如果指定了构造函数,则不再有默认的无参 构造函数,如果需要无参构造函数,则需要自己来写。构造函数可以臓,也就是 有多个参数不同的构造函数。使用构造方法的好处:1.对多个属性进行赋值时,不需要重复的写实例名。2. 可以保证用户在new个对象的时候必须对某个属性进行赋值。3.在new个对象 时,对只读属性进行初始化。在方法定义中,被virtual修饰的方法称为虚函数。是没有实现的,可由子类继承 并重写的函数。在方法定义中,被修饰的方法称为虚函数抽象函数:规定其非虚子类必须实现 的函数,必须被重写> 定义方法一般有以下几种:/ /有返回值方法/调用例子:/int i = 2;/int result = Add(i);/Console. WriteLine(result);/Console. WriteLine(i);/输出后结果为3和2参数为值类型,原值不被修改 public static int Add(int ToNumber) (int sum = 0;for (int i = 1; i <= ToNumber; i+)(sum += i;Ireturn sum;无返回值方法调用例子:/AddVoid(2);/输出后结果为3public static void AddVoid(int ToNumber) (int sum = 0;for (int i = 1; i <= ToNumber; i+)(sum += i;)Console. WriteLine(sum);I引用类型方法调用例子/Test obj = new Test():/AddObject(obj);/Console. WriteLine(obj. Number);输出结果为3传入obj对象的的Number属性,原属性值是1,传入后,对象的属性也被 修改引用类型参数是传地址,不是传值,所有引用类型的原值也会被修改public static void AddObject (Test obj)obj. Number += 2;数组参数方法/调用例子/int Number = 1, 2 ;/int result = AddArray(Number);/Console. WriteLine(result);输出结果为3public static int AddArray(int ToNumber)int num = 0;foreach (var i in ToNumber)num += i;return num;)数组参数简化简化调用/调用例子/int Result = AddArrayV2(l, 2, 3);/Console. WriteLine(Result);输出结果为6这种写法简化数组参数,调用时不需要定义数组,会自动将参数转换为 数组值处理public static int AddArrayV2(params int ToNumber)int num = 0;foreach (var i in ToNumber)num += i;return num;)传地址参数,参数必须初始值/调用例子/int Number = 2;/AddRef(ref Number);/Console. WriteLine(Number);/输出结果为4普通的写法值类型是直接传值,引用类型传地址虽然Number是值类型,但是用ref就变成传地址了,所以原值也会被修改 注意的是,调用的时候也要加ref,而且参数传入前必须有初始值 public static void AddRef(ref int ToNumber)ToNumber += 2;传地址参数,参数无初始值/调用方法/int Number;/AddOut(out Number);/Console. WriteLine(Number);输出结果为3此方法和ref一样,也是将值类型参数传入地址,原值也会被修改与ref不同的是,参数有方法体提供初始值public static void AddOut(out int ToNumber)ToNumber = 1;ToNumber += 2;静态方法和实例方法(定义和调用)静态实例(非静态)static关键字不需要static关键字使用类名调用使用实例对象调用在静态方法中,可以访问静态成员在实例方法中:可以直接访问静态成 员在静态方法中,不可以直接访问实 例成员在实例方法中:可以直接访问实例成 员调用前初始化(*)实例化对象时初始化(*)4 .部分类也可以定义部分方法。部分方法在部分类中定义,但没有方法体,在另 个部分类中执行。在这两个部分类中,都要使用partial关键字。public partial class MyClass(partial void MyPartialMethodO ;)public partial class MyClass(partial void MyPartialMethod()(/ Method implementation)部分方法也可以是静态的,但它们总是私有的,且不能有返回值。它们使用的 任何参数都不能是out参数,但可以是ref参数。部分方法还不能使用virtual、 abstract、override、new、sealed 和 extern 修饰符。有了这些限制,就不太容易看出部分方法的作用了。实际上,部分方法在编译代码 时非常重要,其用法倒并不重要。考虑下面的代码:public partial class MyClass(partial void DoSomethingElse();public void DoSomethingOConsole. WriteLine( uDoSomething() execution started.);DoSomethingElse();Console. WriteLine( "DoSomething() execution finished. n ); i)public partial class MyClass(partial void DoSomethingElse()(Console. WriteLine( "DoSomethingElse() called. n );I:这段代码定义了部分方法DoSomethingElse,在第一个部分类中定义,在第二个部 分类中执行在控制台应用程序中调用DoSomething时,输出如下所示:DoSomething() execution started.DoSomethingElse() called.DoSomething() execution finished.如果删除第二个部分类定义,或者删除部分方法的全部执行代码(注释掉代码),输 出就如下所示:DoSomething() execution started.读者可能认为,调用DoSomethingElse时,运行库发现该方法没有执行代码, 因此会继续执行下一行代码。但实际上,编译代码时,如果代码包含个没有执行 代码的部分方法,编译器会完全删除该方法,还会删除对该方法的所有调用。执行 代码时,不会检查执行代码,因为没有检查方法的调用。这会略微提高性能。与部分类样,在定制自动生成的代码或设计器创建的代码时,部分方法是很 有用的。设计器会声明部分方法,用户根据具体情形选择是否执行它。如果不执行 它,就不会影响性能,因为该方法在编译过的代码中不存在。5 .访问权限访问修饰符:public :可以在任何地方被访问internal :只能在本项目中被访问(在类这个级别,不写访问修饰符时默认的)private :只能在本类中被访问(在类中跟方法中不写访问修饰符时所默认的)preotectd :只能在本类和子类中被访问protected internal :修饰的属性/方法只能在它的在同一个程序集中的子类被访 问不在同一个命名空间下的类,不能直接访问,有2种方法可以访问:1.通过using 引用的命名空间。2.写全称.命名空间.类名在静态方法中,只能访问静态属性,不能直接访问实例属性。在static成员中不能直接调用非static成员,在非static成员中可以调用static成 员。私有成员也会被继承,但是不能被访问。一般用private修饰后让他们看上去 似乎是不能被继承的,但实际上确实被继承了。6.接口 (interface)只含有共有抽象方法(public abstract method)的类这些方法必须在子类 中被实现。定义接口的一般形式为:attributes modifiers interface identifier :base-list interface-body;说明:1、attributes (可选):附加的定义性信息。2、modifiers (可选):允许使用的修饰符有new和四个访问修饰符。 分别是:new、public、protected、internal、privateo 在个接口定义中同一 修饰符不允许出现多次,new修饰符只能出现在嵌套接口中,表示覆盖了继承而来 的同名成员。The public, protected, internal, and private 修饰符定义了对接 的访问权限。3、identifier :接口名称。4、base-list (可选):包含一个或多个显式基接口的列表,接口间由逗号 分隔。5、interface-body :接口主体,个接口的接口主体定义接口的成员。6、接可以是命名空间或类的成员,并且可以包含下列成员的签名:方 法、属性、索引器.7、个接口可从个或多个基接口继承。如:interface IMyExample string thisint index get ; set ; event EventHandler Evenvoid Find (int value);string Point get ; set ; ipublic delegate void EventHandler(object sender, Event e);上面例子中的接口包含个索引this、一个事件Even、个方法Find和一个属 性Point。C#中接口和类的异同:接口和类都是类,不同的是,接口只包含方法或属性的 声明,不包含具体实现方法的代码,接口可以实现多继承,而类只能是单继承,继 承接口的类必须实现接口中声明的方法或属性。接口是一个纯粹的抽象类,没有任何实际的东西,只是定义了一个框架,而抽 象类里面可以有实际的个方法,并不要求所有的方法都是抽象的。可以实现个 接口中的所有方法,也可以继承一个抽象的类,然后覆写其中的方法。接般只 有方法,而没有数据成员或属性。抽象类有方法,也有数据成员或属性,一般情况下, 优先考虑用接口,只有当可能要访问到数据成员或属性时,用抽象类。接口是抽像类的变体。在接口中,所有方法都是抽像的。多继承性可通过实现 这样的接口而获得。接口中的所有方法都是抽像的,没有一个有程序体。接口只可 以定义static final成员变量。接口可以继承接口。接口的成员不能使用internal修饰符。接口可以包含属性、方法、索引指示 器和事件,但不能包含常量、域、操作符、构造函数和析构函数,而且也不能包含 任何静态成员。接口好比种模版,这种模版定义了对象必须实现的方法,其目的 就是让这些方法可以作为接口实例被引用。接口不能被实例化。类可以实现多个接 并且通过这些实现的接口被索引。接口变量只能索引实现该接口的类的实例。C#中的接口与类不同,可以使用多继承,即个子接口可以有多个父接口。但 如果两个父成员具有同名的成员,就产生了二义性(这也正是C#中类取消了多继 承的原因之一),这时在实现时最好使用显式的声明示例:using System;using System. Collections. Generic;using System. Text;namespace Examplel7(class Program个完整的接口声明示例interface lExample属性string Pget;set;方法string F(int Value);事件event EventHandler E;索引指示器string this int Indexget;set;)interface IA'int Count get; set;interface IBint Count ();iIC接口从IA和IB多重继承interface IC : IA, IBclass C : ICprivate int count = 100;显式声明实现IA接口中的Count属性 int IA. Countget return 100; set count = value; )显式声明实现IB接口中的Count方法 int IB. Count ()return count * count;Istatic void Main(string args)C tmpObj = new C();调用时也要显式转换Console. WriteLine Count property- ,(IA) tmpObj). Count);Console. WriteLine( Count function- 0,(IB) tmpObj). Count ();Console. ReadLineO ;结果:Count property: 100Count function: 100007.认识类图Person Class金字段属性 PersonOTeacherClassPerson字段属性 Salary : int才 YearsOfServlce : int方法V SayHi 0 : voidV Teacher0StudentClassPerson五字段T属性 Hobby : string"T Popularity : int,方法M SayHl0 : voidV Student()通用类图类名 Student age;int name; string+ Name:string+SayHi():yoid8. New关键字可以在派生类中隐蔵基类的方法,也就说在使用派生类的方法是 调用的方法是C# New关键字新定义出来的方法,而不是基类的方法。在不使用C# New 关键字来隐蔵基类方法也是可以的,编译器会出现个警告,提示如果有意去隐藏 基类的方法,请使用C# New关键字修饰。学习难点1. String可以看做是char的只读数组,String的字符串的重要特性,不可变型,即 字符串一旦声明就不可改变。所以只能通过索引来读取指定位置的char,不能对指 定位置char进行修改。要想修改可以用以下方式实现:String s = "heklo”;char arr = s. ToCharArray ();arr2= "1";String是密封类,所以不能被继承。String有length。这个方法。数组没有length()这个方法,但有length的属性。2. 当我们实例化一个类时,系统会自动对这个类的属性进行初始化,数字类型初始 为/ , string类型初始为null, char类型初始为:¥0。3. int, datetime, bool, char等类型都属于值类型(ValueType),赋值的时候是 传递拷贝。普通的对象则是引用类型,赋值的时候是传递引用。如:int i=10;int j;i+;Console. WriteLine (j);输出10Person pl = new Person(lO);Person p2 = pl;pl. Age+;Console. WriteLine(p2. Age);输出114. const和readonly有什么区别?答:const可以用于类的成员常量和局部常量,必须在声明时赋值,之后不可以对 常量进行赋值。而readonly只能用于类的成员变量不能用于局部变量,在声明时可 以不赋值,除了在构造函数中可以再次赋值外,其它的地方和const样也不能赋 值。5. 接口和抽象类有什么区别?你选择使用接口和抽象类的依据是什么?答:(1)抽象类可以有抽象成员(无具体实现),也可以有非抽象成员(有具体实 现);接口只有抽象成员。(2) 一个类不能同时继承多个抽象类,个类可以同时实现多个接口。如果多个子类中有很多代码是重复的,这时采用抽象类,因为可以将这些代码放到 抽象类的非抽象方法中,这样可以达到代码复用。当实现多个接口时因为抽象类不 支持,只能使用接口。6 .成员变量和成员函数前加static的作用?答:它们被称为常成员变量和常成员函数,又称为类成员变量和类成员函数。分别 用来反映类的状态。比如类成员变量可以用来统计类实例的数量,类成员函数负责 这种统计的动作。7 .为什么不能指定接口中方法的修饰符?接口中的方法用来定义对象之间通信的契约,指定接口中的方法为私有或保护没有 意义。他们默认为公有方法。常见企业面试题1.class Person 1(int age;public int Ageset get return 18; IIstatic void Main(string args)Personl pl = new Person1 ();pl.Age = 30;pl. Age = pl. Age + 1 ;Console. WriteLine(pl. Age);(输出I8)class Person2int age; public int Age (set (this. Age = value;get return this.Age;static void Main(string args)Person2 p2 = new Person2 ();p2. Age = 30;Console. WriteLine(p2. Age);I(死循环,自己给自己赋值,应p2. age = 30) 2.以下程序有什么错?public class TestClass private int count;private static int y;public static void setvalue ()(count=l;y=i;)答:在静态函数中只能访问静态成员变量;以上代码访问了实例成员。3 .传值和传引用,传引用又两种方式,ref和out, ref要求变量在传参前赋值,out 可以不赋值。传引用必须是个地址,传值是指将栈中的值拷贝到函数的参数,传 引用表示传入的变量和接收参数是同一个变量public class Testpublic int myVar;public int MyProperty(get return myVar; set myVar = value; )以上这个类中有字段和属性i = 100;有一个方法传递引用,调用方式如下Test t = new Test();F (ref t. MyProperty);以上代码编译不能通过,因为属性不是变量,其实质是函数,如下则可通过Test t = new Test ();F(ref t. myVar);因为字段是变量4 .能够将非静态的方法覆写成静态方法么?不能,覆写方法的签名必须与被覆写方法的签名保持一致,除了将virtual改为override。5 .可以覆写私有的虚方法么?不可以,甚至子类中无法访问父类中的私有方法。6 .能够实现允许某个类被继承,但不允许其中的某个方法被覆写么?可以,标记这个类为public,并标记这个方法为sealed。7 . String s = new String ( "xyz”);创建了几个 String Object?答:两个对象,个是"xyz", 个是指向"xyz”的引用对象s。8,在C#中,string str = null 与 string str =""有何区别?答:string str = null是不给他分配内存空间,而string str ="”给它分配长 度为空字符串的内存空间.public static int Count = 0;static Classi()(Count+;public Classi()(Count+;)Classi ol = new Classi();Classi o2 = new Classi ();请问,Classi. Count的值是多少?()10 .以下哪些可以作为接口成员?(多选)A.方法B.属性C.字段D.事件E.索引器F.构造函数G.析构函数(答案ABDE )11 .abstract class BaseClass(public virtual void MethodAO(Ipublic virtual void MethodBO class Classi- BaseClass(public void MethodA(string arg)public override void MethodB()class Class2' Classi(new public void MethodB()(class MainClass(public static void Main (string args)(Class2 二 new Class2 ();Console. Writ eLine (o. MethodA();)请问,o.MethodA调用的是:(A )A. BaseClass. MethodAB. Class2, MethodAC. Classi. MethodAD,都不是集合、比较和转换前面讨论了C#中所有的基本OOP技术,读者还应熟悉些比较高级的技术。本章的主要内容如:集合:集合可以维护对象组。与前面章节使用的数组不同,集合可以包含更高 级的功能,例如,控制对它们包含的对象的访问、搜索和排序等。本章将介 绍如何使用和创建集合类,学习掌握它们的些强大技术。比较:在处理对象时,常常要比较它们。这对于集合尤其重要,因为这是排序 的实现方式。本章将介绍如何以各种方式比较对象,包括运算符重载,使用 IComparable 和 IComparer 接口对集合排序。转换:在前面的章节中,介绍了如何把对象从种类型强制转换为另种类型。 本章讨论如何定制类型转换,以满足自己的要求。11.1 集合第5章介绍了如何使用数组创建包含许多对象或值的变量类型。但数组有一定 的限制。最大的限制是一旦创建好数组,它们的大小就是固定的,不能在现有数组 的末尾添加新项,除非创建一个新的数组。这常常意味着用于处理数组的语法比较 复杂。OOP技术可以创建在内部进行这些处理的类,因此简化了使用项列表或数组 的代码。C#中的数组实现为System.Array类的实例,它们只是集合类中的种。集合类 一般用于处理对象列表,其功能比简单数组要多,这些功能是通过实现 System.Collections名称空间中的接口而获得的,因此集合的语法已经标准化了。这 个名称空间还包含其他些有趣的东西,例如,以与System.Array不同的方式实现 这些接口的类。集合的功能(包括基本函数,例如,用index语法访问集合中的项)可以通过接口 来实现,该接口不仅没有限制我们使用基本集合类,例如System.Array,相反,我们 还可以创建自己的定制集合类。这些集合可以专用于要枚举的对象(即要从中建立集 合的对象)。这么做的一个优点是定制的集合类可以是强类型化的。也就是说,从集 合中提取项时,不需要把它们转换为正确的类型。另个优点是提供专用的方法, 例如,可以提供获得项子集的快捷方法,在扑克牌示例中,可以添加一个方法,获 得特定suit中的所有Card项。System.Collections名称空间中的几个接口提供了基本的集合功能:lEnumerable可以迭代集合中的项。Collection(继承于【Enumerable)可以获取集合中项的个数,并能把项复制到个 简单的数组类型中。IList (继承于lEnumerable和【Collection)提供了集合的项列表,并可以访问这些 项,以及其他些与项列表相关的功能。【Dictionary(继承于【Enumerable和【Collection)类似于【List,但提供r可通过键 码值而不是索引访问的项列表。System.Array 类实现了【ist、【Collection 和【Enumerable,但不支持 IList 的一些 更高级的功能,它表示大小固定的项列表。11.1.1 使用集合Systems.Collections 名称空间中的类 System.Collections.ArrayList 也实现了【List、 ICollection和!Enumerable接口,但实现的方式比System.Array更复杂。数组的大小 是固定的(不能增加或删除元素),而这个类可以用于表示大小可变的项列表。为了 更准确地理解这个高级集合的功能,下面介绍一个使用这个类和一个简单数组的示 例。试试看:数组和高级集合(1)在目录C:BegVCSharpChapterl 1'下仓建一个新的控制台应用程序 ChllExOlo(2)在Solution Explorer窗口中右击项目,选择Add I Class选项,给项目添加3 个新类:Animal > Cow 和 Chicken。(3)修改Animal.cs中的代码,如下所示:namespace ChilExOlpublic abstract class Animalprotected string name;public string Name( get return name; set(name = value;public Animal()(name = "The animal with no name"public Animal(string newName)( name = newName;public void Feed()Console.WriteLine("0 has been fed”, name);修改Cow.cs中的代码,如下所示:namespace ChllExOl (public class Cow : Animalpublic void Milk() (Console.WriteLine("0 has been milked.", name); )public Cow(string newName) : base(newName) ( ) 修改Chicken.cs中的代码,如下所示:nam