资源描述
对象、类和结构概述对象、类和结构具有以下特点:新数据类型是使用类和结构定义的。对象是给定数据类型(类和结构)的实例。在执行应用程序时,数据类型为创建对象(或实例化)提供蓝图。C#应用程序始终包含至少一个类。结构可视为轻量类,是创建用于存储少量数据的数据类型的理想选择,不能通过继承进行扩展的类型。类支持继承,这意味着它们可以从先前定义的类中派生。典型的 C#应用程序由自定义的类和.NET框架 的类组成。类是 C#中功能最为强大的数据类型。类定义了数据类型的数据和行为。对对象象、类类和和结结构构第1页/共54页结构(struct)结构与类一样都具有表示其数据和行为的成员。这些成员包括:字段,属性,方法,事件,运算符,索引器,构造函数,析构函数和嵌套类型。结构与类共享几乎所有相同的语法,但结构比类受到的限制更多。结构有以下特点:结构是值类型,而类是引用类型。如果从结构创建一个对象并将该对象赋给某个变量,变量则包含结构的全部值。复制包含结构的变量时,将复制所有数据,对新副本所做的任何修改都不会改变旧副本的数据。尽管结构的静态字段可以初始化,结构实例字段声明还是不能使用初始值设定项。结构不能声明默认构造函数(没有参数的构造函数)或析构函数。与类不同,结构的实例化可以不使用 new 运算符。一个结构不能从另一个结构或类继承,而且不能作为一个类的基。所有结构都直接继承自 System.ValueType,后者继承自 System.Object。结构可以实现接口。由于结构不使用引用,因此结构没有标识,具有相同数据的两个值类型实例是无法区分的。对对象象、类类和和结结构构第2页/共54页对象对象指的是一个实体的实例,在这个实体中包括了特定的属性数据和对这些数据进行操作的方法。对象具有以下特点:C#中使用的全都是对象,包括 Windows 窗体和控件。对象是实例化的;也就是说,对象是从类和结构所定义的模板中创建的。对象使用属性获取和更改它们所包含的信息。对象通常具有允许它们执行操作的方法和事件。Visual Studio 提供了操作对象的工具:使用“属性”窗口可以更改对象(如 Windows 窗体)的属性。使用对象浏览器可以检查对象的内容。所有 C#对象都继承自object。对对象象、类类和和结结构构第3页/共54页类声明类是使用class关键字来定义的,类声明语句定义一个新类,其语法格式为:特征 修饰符 class 类名:基类名和任何实现接口的列表类体类的主要修饰符有以下几种之一。public 表示不限制对该类的访问protected 表示只能从所在类和所在类派生的子类进行访问private只有其所在类才能访问abstract 抽象类不允许建立类的实例internal 只有在同一程序集(.exe 或.dll)的文件中的类型或成员才可访问的partial 表示将类的定义拆分到两个或多个源文件中默认情况下,在命名空间中或在编译单元顶部(例如,不在命名空间、类或结构中)声明的任何类型都是internal。下面是声明类Shape的代码:对对象象、类类和和结结构构第4页/共54页abstract class Shape/抽象基类,不可实例化public const double pi=System.Math.PI;/常量protected double x,y;/受保护,可继承变量public Shape()/默认构造函数x=y=0;public Shape(double x,double y)/带参数构造函数this.x=x;this.y=y;public abstract double Area();/抽象方法,需重载在该例中 abstract class Shape类定义使用了修饰符abstract,这表示该类是抽象基类,不可实例化。类的名称位于 class 关键字的后面,就是Shape。Shape类定义的其余部分是类的主体,用于定义行为和数据。类的字段、属性、方法和事件统称为“类成员”。第5页/共54页分部类定义可以将类、结构或接口的定义拆分到两个或多个源文件中。每个源文件包含类定义的一部分,编译应用程序时将把所有部分组合起来。拆分类定义,使用 partial 关键字修饰符,partial 修饰符只能出现在紧靠关键字 class、struct 或 interface 前面的位置。如下例所示。Visual Studio 在创建 Windows 窗体使用此分部定义方法,例如:下面在form1.cs和Form1.Designer.cs代码文件中使用分部定义共同定义类Form1。其中Form1.Designer.cs是窗体设计器自动生成的源代码,而form1.cs是手工编程代码的文件。public partial class Form1:Form /form1.cs 文件partial class Form1 /Form1.Designer.cs对对象象、类类和和结结构构第6页/共54页类继承定义继承是类的重要特性。类可以从其他类中继承。在声明类时,在类名称后放置一个冒号,然后在冒号后指定要从中继承的类(即基类)。例如定义一个继承了Shape类的Ellipse类:class Ellipse:Shape public Ellipse(double x,double y):base(x,y)/使用基类Shape的构造函数 public override double Area()/函数重载 return pi*x*y;使用继承定义的类称为用派生类,派生类的成员包括基类的所有非私有数据和行为以及派生类为自己定义的其他数据或行为。派生类将无法继承基类私有的成员。例如:派生类Ellipse继承基类Shape的公有的常量pi和受保护的字段x,y。对对象象、类类和和结结构构第7页/共54页字段类的字段存储类要满足其设计所需要的数据。声明字段时可以使用赋值运算符为字段指定一个初始值。字段恰好在创建对象实例调用构造函数之前初始化。字段初始值设定项不能引用其他实例字段。字段可标记为public、private、protected、internal。这些修饰符定义类的使用者访问字段的方式。可以选择将字段声明为static静态,这使得在没有创建实例能调用静态字段。常数被声明为字段,声明时在字段的类型前面使用const关键字。常数必须在声明时初始化。例如:public const double pi=System.Math.PI;只读字段将字段声明为readonly。只读字段只能在初始化期间或在构造函数中赋值。为了简单起见,下面示例使用public字段,这样可以直接获取或设置字段值,但是不建议这样做。字段通常应为private。外部类应当通过方法、属性或索引器来间接访问字段。例如:对对象象、类类和和结结构构第8页/共54页字段public class Point public double x,y;/声明字段 public double z=0;/声明字段具有初始值。public readonly string name=Point;/声明具有初始值只读字段。public Point(double x,double y)/构造函数用于初始化字段。this.x=x;this.y=y;name=string.Format(0(1,2,3),name,x,y,z);public Point():this(0,0)/调用其他构造函数对对象象、类类和和结结构构第9页/共54页构造函数构造函数是在创建给定类型的对象时执行的类的方法。构造函数具有与类或结构相同的名称,它通常用于初始化新对象的数据成员。只要创建类或结构,就会调用它的构造函数。类或结构可能有多个接受不同参数的构造函数。在上面的示例中,定义了一个具有一个简单的构造函数名为Point的类。类定义对象的类型,但它不是对象本身。对象是基于类的具体实体,有时称为类的实例。通过使用 new 关键字,后跟类的构造函数,可以创建对象,在为新对象分配内存之后,new 运算符立即调用构造函数。下面的TestPoint类Main()中使用 new 运算符创建对象来实例化Point类:class TestPoint static void Main()Point p=new Point(1.0,3.0);/创建Point对象实例p p.z=1;/Point对象实例p的z字段赋值 Console.WriteLine(x,y,z)=0,1,2,3,p.x,p.y,p.z,p.name);对对象象、类类和和结结构构第10页/共54页构造函数不带参数的构造函数称为“默认构造函数”。当类没有构造函数,系统自动生成一个构造函数,并且将用默认值来初始化对象字段,有关默认值参见表2.1。构造函数可以使用 this 关键字调用同一类中的另一构造函数。如public Point():this(0,0)/调用其他构造函数基类的构造函数将在其派生类构造函数调用前调用,使用:base(x,y)表示调用基类的构造函数。在前面示例中,抽象基类Shape的构造函数在执行派生类Ellipse构造函数块之前被调用,使用:base(x,y)表示调用基类Shape的构造函数。public Ellipse(double x,double y):base(x,y)/使用基类Shape的构造函数在派生类中,如果不使用base关键字来显式调用基类构造函数,则将隐式调用默认构造函数(如果有的话)。这意味着下面的构造函数隐式调用基类Shape默认构造函数:public Ellipse(double x,double y)/隐式调用基类Shape的构造函数:base()如果基类没有提供默认构造函数,派生类必须使用 base 显式调用基构造函数。如没有默认构造函数,前面不使用base 关键字的派生类构造函数将无法编译。对对象象、类类和和结结构构第11页/共54页析构函数在类的实例超出范围时,类的实例将会自动执行析构函数。例如:Shape()/析构函数,对象销毁时的清除语句.Console.WriteLine(调用Shape析构函数(0,1),x,y);Ellipse()/析构函数 Console.WriteLine(调用Ellipse析构函数(0,1,Area2:f),x,y,Area();析构函数特点:一个类只能有一个析构函数。无法继承或重载析构函数。无法调用析构函数。它们是被自动调用的。析构函数既没有修饰符,也没有参数。可以通过调用GC.Collect()强制进行垃圾回收,但大多数情况下应避免这样做,因为这样会导致性能问题。调用对象结束将按照从派生程度最大的到派生程度最小的次序调用析构函数。对对象象、类类和和结结构构第12页/共54页方法声明方法是包含一系列语句的代码块,是类或结构中用于执行计算或其它行为的成员。在 C#中,每个执行指令都是在方法中完成的。方法在类或结构中声明,声明时需要指定访问级别、返回值、方法名称以及任何方法参数。方法参数放在括号中,并用逗号隔开。空括号表示方法不需要参数。方法的声明格式:特征 修饰符 方法名称(形式化参数表)方法体 方法的修饰符可以是(public,protected,internal,private,static,abstract,virtual,override等)及其组合。例如下面例子的Area方法:Shape类:public abstract double Area();/抽象方法,需要在派生类重载该方法Ellipse类:public override double Area()/派生类重载基类抽象方法第13页/共54页方法的参数C#中方法的参数有以下两种类型:值传递类型参数:不含任何修饰符。如:void SquareIt(int x)引用传递类型参数:以ref 或out 修饰符声明。如:void SquareIt(ref int x)方法值传递类型参数意味着向方法传递变量的一个副本。方法内发生的参数更改对该变量中存储的原始数据无任何影响。而引用传递类型参数意味所调用的方法可更改参数的原始数据值。向方法传递结构和类引用之间的区别:向方法传递结构时,传递的是该结构的副本,而在传递类实例时,传递的是一个引用。传递类型参数修饰符out与ref 的区别在于:传递到 ref的参数必须先初始化。而out 的参数在传递之前不需要显式初始化。值传递有以下两种类型:值传递值类型,如:void SquareIt(int x)参数int x 是值类型。值传递引用类型,如:void Change(int pArray)数组pArray 为引用类型。引用传递有以下两种类型:引用传递值类型,如:void SquareIt(ref int x)引用传递引用类型,如:void Change(ref int pArray)方法第14页/共54页值传递值类型参数示例(ParamSample项目代码):class PassingValByVal static void SquareIt(int x)x*=x;System.Console.WriteLine(SquareIt方法内的n值:0,x);static void Main()int n=5;System.Console.WriteLine(调用SquareIt方法前的n值:0,n);SquareIt(n);/通过值来传递值 System.Console.WriteLine(调用SquareIt方法后的n值:0,n);该程序运行输出:调用SquareIt方法前的n值:5 SquareIt方法内的n值:25 调用SquareIt方法后的n值:5 在这例子里,变量 n 为值类型,包含数据(值为 5)。当调用 SquareIt 时,n 的内容被复制到参数 x 中,在方法内将该参数求平方。但在 Main 中,n 的值在调用 SquareIt 方法前后是相同的。实际上,方法内发生的更改只影响局部变量 x。第15页/共54页值传递引用类型例子:class PassingRefByVal static void Change(int pArray)pArray0=888;/将pArray的第一个元素赋值为888,改变arr0 pArray=new int5 -3,-1,-2,-3,-4;/引用新的数组,不改变arr System.Console.WriteLine(方法内的数组第一元素:0,pArray0);static void Main()int arr=1,4,5;System.Console.WriteLine(调用方法前的数组第一元素:0,arr0);Change(arr);System.Console.WriteLine(调用方法后的数组第一元素:0,arr0);在上个示例中,数组 arr 为引用类型,在未使用 ref 参数的情况下传递给方法。在此情况下,将向Change方法传递指向 arr 的引用的一个副本。Change方法有可能更改数组元素的内容,输出显示arr0 从 1 改为 888。但是,在 Change 方法内使用 new 运算符来分配新的内存部分,将使变量 pArray 引用新的数组。因此,这之后的任何更改都不会影响原始数组 arr(它是在 Main 内创建的)。该程序运行输出:调用方法前的数组第一元素:1方法内的数组第一元素:-3调用方法后的数组第一元素:888第16页/共54页引用传递值类型示例 class PassingValByRef/参数X的值通过关键字ref来传递,改变x的值,将会改变输入的x的值 static void SquareIt(ref int x)x*=x;System.Console.WriteLine(SquareIt方法内的n值:0,x);static void Main()int n=5;System.Console.WriteLine(调用SquareIt方法前的n值:0,n);SquareIt(ref n);/通过ref关键字来传递变量引用.System.Console.WriteLine(调用SquareIt方法后的n值:0,n);该程序运行输出:调用SquareIt方法前的n值:5 SquareIt方法内的n值:25 调用SquareIt方法后的n值:25 本示例中,传递的不是 n 的值,而是对 n 的引用。参数 x 不是 int 类型,它是对 int 的引用(本例中为对 n 的引用)。因此,当在方法内对 x 求平方时,实际被求平方的是 x 所引用的项:n。第17页/共54页引用传递引用类型示例:class PassingRefByRef static void Change(ref int pArray)/下面两个数组赋值都将改变原始的元素 pArray0=888;/将pArray的第一个元素赋值为 pArray=new int5 -3,-1,-2,-3,-4;/pArray 引用新的数组 System.Console.WriteLine(Change方法内的数组第一元素:0,pArray0);static void Main()int arr=1,4,5;System.Console.WriteLine(调用Change方法前的数组第一元素:0,arr0);Change(ref arr);/通过ref关键字来传递数组引用 System.Console.WriteLine(调用Change方法后的数组第一元素:0,arr0);该程序运行输出:调用Change方法前的数组第一元素:1Change方法内的数组第一元素:-3调用Change方法后的数组第一元素:-3本示例中Change方法内发生的所有更改都影响 Main 中的原始数组。实际上,使用 new 运算符对原始数组进行了重新分配。因此,调用 Change 方法后,对 arr 的任何引用都将指向 Change 方法中创建的五个元素的数组。第18页/共54页参数数组params当方法的参数数量不确定时,可以考虑使用参数数组params作为方法参数,如:void UseParams(params int list);在方法声明中的 params 关键字之后不允许任何其他参数,并且在方法声明中只允许一个 params 关键字。项目params1的代码如下:public class MyClass public static void UseParams(params int list)for(int i=0;i list.Length;i+)Console.WriteLine(listi);public static void UseParams2(params object list)for(int i=0;i list.Length;i+)Console.WriteLine(listi);static void Main()UseParams(1,2);/可以使用不同数目的整数作为参数 UseParams2(1,a,test);/可以使用不同数目的任何类型值作为参数 int myarray=new int3 10,11,12;UseParams(myarray);/可以使用数组作为参数 第19页/共54页类的静态成员和静态类静态类和类成员用于创建无需创建类的实例就能够访问的数据和方法。静态类成员可用于独立于任何对象标识的数据和行为:无论对象发生什么更改,这些数据和函数都不会随之变化。静态类成员是一种特殊的成员,它不属于类的某一个具体的实例。当类只有静态成员时就可以使用静态类。类的静态成员在成员的返回类型之前使用 static 关键字来声明静态类成员。即使没有创建类的实例,也可以调用该类中的静态方法、字段、属性或事件。如果创建了该类的任何实例,不能使用实例来访问静态成员。静态成员通常用于表示不会随对象状态而变化的数据或计算。非静态方法可以访问类中的静态或非静态成员,而静态方法只能访问类中的静态成员。静态类:当类中没有依赖类的特定对象的数据或行为时,即仅包含静态成员,就可以使用。静态类声明为static,以指示它仅包含静态成员的静态类。不能使用 new 关键字创建静态类的实例。静态成员和静态类第20页/共54页类静态成员例子class classA int x;static int y;static public void F()/x=1;错误不允许访问 y=2;/正确允许访问 class TestA static void Main()classA aClass=new classA();/创建类A的实例aObject /aClass.F();/错误,不能使用类A的实例aObject来访问静态成员 classA.F();/正确,使用类来访问静态成员 静态成员和静态类第21页/共54页静态类例子public static class TemperatureConverter/静态类/静态方法 public static double CelsiusToFahrenheit(string temperatureCelsius)/将string型的temperatureCelsius转化成double型 double celsius=System.Double.Parse(temperatureCelsius);/将计算结果也转换成double型 double fahrenheit=(celsius*9/5)+32;return fahrenheit;class TestTemperatureConverter static void Main()/调用静态类的静态方法 double F=TemperatureConverter.CelsiusToFahrenheit(38.9);静态成员和静态类第22页/共54页继承多态性通过继承,一个类可以用作多种类型:可以用作它自己的类型、任何基类型,或者在实现接口时用作任何接口类型。这称为多态性。C#中的每种类型都是多态的。类型可用作它们自己的类型或用作 Object类型,因为任何类型都自动将 Object 当作基类型。多态性不仅对派生类很重要,对基类也很重要。任何情况下,使用基类实际上都可能是在使用已强制转换为基类类型的派生类对象。当派生类从基类继承时,它会获得基类的所有非私有的方法、字段、属性和事件。若要更改基类的数据和行为,有两种选择:可以使用新的派生成员替换基成员,或者可以重写虚拟的基成员。继继承承第23页/共54页使用new关键字派生类可以包含与基类方法名称相同的数据和方法成员。使用新的派生成员替换基类的成员需要使用 new 关键字。如果基类定义了一个方法、字段或属性,则new关键字用于在派生类中创建该方法、字段或属性的新定义。new 关键字放置在要替换的类成员的返回类型之前。例如:public class BaseClass public void DoWork()Console.WriteLine(Base DoWork);public int WorkField;public int WorkProperty get return 0;public class DerivedClass:BaseClass public new void DoWork()Console.WriteLine(Derive DoWork);public new int WorkField;public new int WorkProperty get return 1;继继承承第24页/共54页使用new关键字使用new关键字时,调用的是新的类成员而不是已被替换的基类成员。这些基类成员称为隐藏成员。如果将派生类的实例强制转换为基类的实例,就仍然调用隐藏类成员。例如:public class test static void Main()DerivedClass B=new DerivedClass();B.WorkField=2;B.DoWork();/调用DerivedClass方法DoWork().显示“Derive DoWork”Console.WriteLine(B.WorkField=0,B.WorkField);/显示“B.WorkField=2”BaseClass A=(BaseClass)B;/A.WorkField未赋值,为int的默认值 A.DoWork();/调用BaseClass的方法DoWork().显示“Base DoWork”Console.WriteLine(A.WorkField=0,A.WorkField);/显示“A.WorkField=0”继继承承第25页/共54页使用virtual和override关键字为了使派生类的实例完全接替来自基类的类成员,基类必须将该成员声明为虚拟的。这是通过在该成员的返回类型之前添加 virtual 关键字来实现的。字段不能是虚拟的,只有方法、属性、事件和索引器才可以是虚拟的。然后,派生类可以选择使用 override 关键字而不是 new,将基类实现替换为它自己的实现。当派生类重写某个虚拟成员时,即使该派生类的实例被当作基类的实例访问,也会调用派生类的成员,而在派生类可以使用 base 关键字访问基类的成员,例如:public class BaseClass public virtual void DoWork()Console.WriteLine(Base DoWork);public virtual int WorkProperty get return 0;继继承承第26页/共54页public class DerivedClass:BaseClass public override void DoWork()base.DoWork();/调用BaseClass方法DoWork()显示Base DoWork Console.WriteLine(Derive DoWork);public override int WorkProperty get return 1;public class test static void Main()DerivedClass B=new DerivedClass();/调用重写方法DoWork().显示Base DoWork和“Derive DoWork”B.DoWork();/显示B.WorkProperty=1”Console.WriteLine(B.WorkProperty=0,B.WorkProperty);BaseClass A=(BaseClass)B;A.DoWork();/调用重写方法DoWork().显示“Derive DoWork”/显示B.WorkProperty=1”Console.WriteLine(A.WorkProperty=0,A.WorkProperty);第27页/共54页abstract抽象类使用 abstract 关键字可以创建仅用于继承用途的类和类成员。将类声明为抽象类的方法是在类定义中将关键字 abstract 置于关键字 class 的前面。例如:abstract class Shape;抽象类不能实例化。抽象类的用途是提供多个派生类可共享的基类的公共定义。在抽象类中可以定义抽象方法。方法是将关键字 abstract 添加到方法的返回类型的前面。例如:public abstract double Area();抽象方法没有实现,所以方法定义后面是分号,而不是常规的方法块。抽象类的派生类必须实现所有抽象方法。继继承承第28页/共54页sealed密封类和类成员可以将类声明为sealed密封类。密封类主要用于防止派生。声明方法是在类定义中将关键字 sealed 置于关键字 class 的前面。例如:public sealed class sClass 类定义体。密封类不能用作基类。因此,它也不能是抽象类。在对基类的虚成员进行重写的派生类上的类成员(包括方法、字段、属性或事件)可以将该成员声明为密封成员。在用于以后的派生类时,这将取消成员的虚效果。方法是在类成员声明中将 sealed 关键字置于 override 关键字的前面。例如:public class sClass:bClass public sealed override void DoWork()下面abstractShape项目的代码演示 Shape 的多个子类,并演示它们如何重写 Area 属性来提供自己的实现。然后创建若干 Shape 派生对象,并输出它们的面积。继继承承第29页/共54页public abstract class Shape private string m_id;public Shape(string s)Id=s;public string Id/属性Id get return m_id;set m_id=value;public abstract double Area get;/抽象只读属性Area public override string ToString()return Id+Area=+string.Format(0:F2,Area);public class Square:Shape private int m_side;/正方形边长 public Square(int side,string id):base(id)m_side=side;public override double Area get return m_side*m_side;/正方形面积边长边长第30页/共54页public class Rectangle:Shape private int m_width;private int m_height;public Rectangle(int width,int height,string id):base(id)m_width=width;m_height=height;public override double Area getreturn m_width*m_height;/长方形面积class TestClass static void Main()Shape shapes=new Square(5,Square#1),new Rectangle(4,5,Rectangle#1);foreach(Shape s in shapes)/下句相当于System.Console.WriteLine(s.ToString();System.Console.WriteLine(s);第31页/共54页接口继承接口具有下列属性:接口类似于抽象基类:继承接口的任何非抽象类型都必须实现接口的所有成员。不能直接实例化接口。接口可以包含事件、索引器、方法和属性。接口不包含方法的实现。类和结构可从多个接口继承。接口自身可从多个接口继承。继继承承第32页/共54页重载运算符用户定义的类型可以通过使用 operator 关键字定义静态成员函数来重载运算符。下表列出了可重载的运算符:下面定义一个DBBool结构,主要运用了以下的转换运算符定义和重载运算符:使用implicit隐式转换运算符定义了从bool到DBBool的隐式转换。使用explicit显式转换运算符定义了从DBBool到bool的强制转换.定义重载运算符=、!=、!、&、|、true、false、Equals等。dbBool项目的代码如下:+、-、!、+、-、true 和 false可以重载这些一元运算符。+,-,*,/,%,&,|,可以重载这些二进制运算符。=,!=,=比较运算符可以重载(但要对称重载,如果重载=,也必须重载!=)。继继承承第33页/共54页public struct DBBool/三个可能的 DBBool 值:public static readonly DBBool dbNull=new DBBool(0);public static readonly DBBool dbFalse=new DBBool(-1);public static readonly DBBool dbTrue=new DBBool(1);int value;/为 dbFalse、dbNull、dbTrue 存储-1、0、1 的私有字段:DBBool(int value)/私有构造函数。值参数必须为-1、0 或 1:this.value=value;/从bool到DBBool的隐式转换。将true映射为DBBool.dbTrue,/将false映射BBool.dbFalse public static implicit operator DBBool(bool x)return x?dbTrue:dbFalse;/从 DBBool 到 bool 的显式转换。如果 /给定的 DBBool 为 dbNull,则引发异常;否则返回 true 或 false:public static explicit operator bool(DBBool x)if(x.value=0)throw new InvalidOperationException();return x.value 0;public static DBBool operator=(DBBool x,DBBool y)if(x.value=0|y.value=0)return dbNull;return x.value=y.value?dbTrue:dbFalse;第34页/共54页/不等运算符。如果任何一个操作数为 /dbNull,则返回 dbNull;否则返回 dbTrue 或 dbFalse:public static DBBool operator!=(DBBool x,DBBool y)if(x.value=0|y.value=0)return dbNull;return x.value!=y.value?dbTrue:dbFalse;/逻辑 AND 运算符。如果任何一个操作数为dbFalse,则返回 dbFalse;/如果任何一个操作数为 dbNull,则返回 dbNull;否则,返回 dbTrue:public static DBBool operator&(DBBool x,DBBool y)return new DBBool(x.value y.value?x.value:y.value);/真true运算符。如果操作数为dbTrue,则返回 true,否则返回 false:public static bool operator true(DBBool x)return x.value 0;/假false运算符。如果操作数为dbFalse,则返回 true,否则返回 false:public static bool operator false(DBBool x)return x.value 0?dbTrue:x.value 0?dbFalse:dbNull;第35页/共54页 public override bool Equals(object o)/重写Object.Equals(object o)方法 return(bool)(this=(DBBool)o);/重写 ToString 方法以便将 DBBool 转换为 string:public override string ToString()return(string)this;class Test static void Main()DBBool a,b,c,d;a=DBBool.dbTrue;b=DBBool.dbNull;c=false;/从 bool 到 DBBool 的隐式转换 bool blnA=(bool)a;/从 DBBool 到 bool 的显式转换 d=(a=b);/相等运算符=Console.WriteLine(!0=1,a,!a);Console.WriteLine(string)a=0,(string)a);Console.WriteLine(0&1=2,a,b,a&b);Console.WriteLine(0|1=2,a,b,a|b);if(b)/调用真运算符以确定 DBBool 变量的布尔值:Console.WriteLine(b is definitely true);第36页/共54页属性的定义 属性是这样的成员:它们提供灵活的机制来读取、编写或计算类或结构的私有字段的值。可以像使用公共数据成员一样使用属性,但实际上它们是称为“访问器”的特殊方法。这使得数据在可被轻松访问的同时,仍能提供方法的安全性和灵活性。属性的定义形式为:特征 修饰符 属性名属性访问器。其中属性访问器有两种:get访问器和set访问器。例如:public string Name /名字属性 get return name;set name=value;get访问器是一个不带参数的方法,它用于向外部返回属性成员的值。它的基本格式为:修饰符 get访问体。get访问器必须以一条return语句结束,或者是一条异常抛出语句throw。set访问器是一个带有单个参数并且返回类型为void类型的方法,它用于处理类外部的写入值,其中参数类型就是属性的类型。它的基本格式为:修饰符 set访问体。set访问器带有一个特殊的关键字value。用以传递属性赋值的内容。属属性性与与索索引引器器第37页/共54页使用属性属性结合了字段和方法的多个方面,以字段形式出现,用方法来实现。对于对象的使用者,属性显示为字段,访问属性与字段需要完全相同的语法。一个属性不一定需要既具备get访问器,又具备set访问器。不具有 set 访问器的属性被视为只读属性。不具有 get 访问器的属性被视为只写属性。同时具有这两个访问器的属性是读写属性。与字段不同,属性不作为变量来分类。因此,不能将属性作为 ref参数或 out参数传递。可以使用 static 关键字将属性声明为静态属性。这使得调用方随时可使用该属性,即使不存在类的实例。属性具有多种用法:它们可在允许更改前验证数据;它们可公开某个类上的数据,该类的数据实际上是从其他源(例如数据库)检索到的;当数据被更改时,它们可采取行动,例如引发事件或更改其他字段的值。如下例Employee类的部分属性:属属性性与与索索引引器器第38页/共54页使用属性class Employee static private int count;/员工数 private string name;/姓名 private CertificateType certificate;/拥有从业资格 public static int Count/静态员工数只读属性 get return count;public string Name /名字属性 get return name;set name=value;属属性性与与索索引引器器第39页/共54页接口属性 可以在接口上声明属性。接口属性的访问器不具有实现代码。因此,访问器的用途是指示属性是否为读写、只读或只写。在定义实现一个接口的类时,使用接口属性的完全限定名,称为显式接口实现,显式接口的实现可用于区别不同接口具有相同属性。例如:interface IEmployee string Name get;set;/属性声明 int Counter get;public class Employee:IEmployee private string name;public string Name /读写属性 get return name;set name=value;private int counter;public int IEmployee.Counter /只读属性显式接口实现 get return counter;属属性性与与索索引引器器第40页/共54页索引器索引器允许类或结构的实例按照与数组相同的方式进行索引。索引器类似于属性,不同之处在于它们的访问器采用参数。索引器的定义格式如下:特征 修饰符 类型 this索引参数表 get 代码体 set 代码体 下面的IndexSample项目的代码示例说明如何声明私有数组字段arr 和索引器。使用索引器可直接访问实例testi。属属性性与与索索引引器器第41页/共54页class IndexerClass private int arr=new int100;public int thisint index /定义索引器 get /设置索引范围 if(index=100)return 0;else return arrindex;set if(!(index=100)arrindex=value;static void Main()IndexerClass test=new IndexerClass();test3=256;/通过索引整数3和5 来调用索引器.test105=1024;/因105=100,所以不改变数组值.for(int i=0;i“类”,在对话框名称处输入Employee.cs,按确定,即创建Employee类。右击项目Employee.cs节点,弹出菜单选择“查看类关系图”,生成类关系图ClassDiagram1.cd,包含类Employee。如下图.第47页/共54页2.创建枚举SexType和结构CertificateType,分别用于员工Employee类的性别和从业资格成员类型。可以在类关系图右击弹出菜单选择“添加”-“枚举”或“结构”来设计,也可在Employee.cs中直接编码如下:下面通过类关系图设计类、枚举和结构三个类型。第48页/共54页enum Sextype 男,女,空;/性别枚举struct CertificateType/从业资格结构类型 public bool blnTransact;/是否有交易资格 public bool blnIPO;/是否有发行资格 public bool blnAnalyse;/是否有分析资格 public CertificateType(bool blnTransact,bool blnIPO,bool blnAnalyse)this.blnTransact=blnTransact;this.blnIPO=blnIPO;this.blnAnalyse=blnAnalyse;3.根据员工表共有八栏信息,分别为Employee类添加添加8个字段,在类关系图右击Employee类弹出菜单选择“添加”-“字段”来设计,或直接编码如下:class Employee private string name;/姓名 private Sextype sex;/性别 private string dept;/部门 private DateTime enterDate;/进入日期 private string addr;/地址 private string mobile;/手机号 private CertificateType certificate;/拥有从业资格 private string memo;/备注第49页/共54页4.为Employee类添加添11个属性,在类关系图右击Employee类弹出菜单选择“添加”-“属性”来设计,或右击某字段弹出菜单选择“重构”-“封装字段”来设计,或直接编码如下:(完全代码请看Employee.cs)public string Name/名字属性 get return name;set name=value;public string HasTransact/是否有交易资格属性 get return this.certificate.blnTransact?是:否;set if(value=是)certificate.blnTransact=true;else if(value=否)certificate.blnTransact=false;5.为Employee类添加静态方法和构造涵数,在类关系图右击Employee类弹出菜单选择“添加”-“构造涵数“和“方法”来设计,或直接编码如下:第50页/共54页static public Sextype getSextype(bool blnMan,bool blnWoman)/获取性别类型 Sextype aSex;if(blnMan)aSex=Sextype.男;else if(blnWoman)aSex=Sextype.女;else aSex=Sextype.空;return aSex;public Employee(string name,Sextype sex,string dept,DateTime enterDate,string addr,string mobile,CertificateType certificate,string memo)this.name=name;this.sex=sex;this.dept=dept;this.enterDate=enterDate;this.addr=addr;this.mobile=mobile;this.certificate=certificate;this.memo=memo;编写EmployeeForm窗体事件代码,1.在EmployeeForm类添加方法reset():用于清空显示员工信息的控件内容和lvEmployeeReset()用于员工列表视图添加表头和清空:略。第51页/共54页2.在EmployeeForm类构造涵数中添加方法lvEmployeeReset(),reset()以初始化窗体。public EmployeeForm()InitializeComponent();Reset();lvEmployeeReset();3.添加btnClear按钮click事件代码,同时用于工具栏清空按钮和新建员工菜单项事件处理代码。private void btnClear_Click(object sender,EventArgs e)this.Reset();4.添加方法lvEmployeeAdd(Employee emp),使用Employee类对象为参数,将其添加到列表视图。private void lvEmployeeAdd(Employee emp)/向列表视图中添加项 ListViewItem lvi=new ListViewItem(emp.Name);lvi.Tag=emp;/在节点保存Employee对象 lvi.SubItems.Add(emp.Sex.ToString();lvi.SubItems.Add(emp.Dept);lvi.SubItems.Add(emp.EnterDate.ToShortDateString();/略 lvEmployee.Items.Add(lvi);第52页/共54页5.添加btnAdd按钮click事件代码,同时用于工具栏添加员工按钮和添加员工菜单项事件处理代码。private void btnAdd_Click(object sender,EventArgs e)if(txtName.Text=)MessageBox.Show(请输入姓名);return;if(lvEmployee.FindItemWithText(txtName.Text)!=null)MessageBox.Show(已有该员工资料);return;/取的性别类型 Sextype empSex=Employee.getSextype(rbMan.Checked,rbWoman.Checked);CertificateType empCerticate=new CertificateType(clstCertificate.GetItemChecked(0),clstCertificate.GetItemChecked(1),clstCertificate.GetItemChecked(2);/取得从业资格结构类型 Employee
展开阅读全文