C#的类与结构,以及面向对象编程思想

时间:2023-02-15 14:18:54
讲这节课程之前我首先阐述几个概念 对象是什么﹐对象就是这个世界的全部﹐人﹑植物﹑星体等等 类是什么﹐类是对对象的一个抽象﹐把有共同特征行为的对象抽象在一起就是类﹐目的是为了更好的运做管理这些对象   我们进一步﹐把这些上升到我们需要的高度﹕ 类是什么﹐对于有同样属性、共同的行为、共同的联系和共同的语义的对象的描述。它是面向对象的的基本组成。 通俗一点﹐类是一种数据类型﹐但是它是复杂的抽象的﹐它是将不同类型的数据和与这些数据相关的操作封装在一起的集合体。有点象C中的结构体﹐但是远远比那个要复杂。 对象是什么﹐对像是类的一个实例﹐是我们编写程序真正控制的东西﹐即实体。   进而 面向对象是什么﹐我们写程序﹐所要控制的东西都是对现实世界中的对象的抽象﹐那么当我们编程时候如果是以(也就是面对)对象以及对象的行为作为整体操作时﹐那么我们这种编程的方式就面向对象的。 面向对象是什么﹐我认为它是一种思想一种方法﹐是一个广泛使用但涵义并不清晰的术语﹐人人都在说人人都说不清楚的东西﹐面向对象主要指一种程序设计方法,且它在不断的发展中﹐我不能给你一个准确的定义。   虽然不好表述它﹐但是它有基本特征﹕继承性﹑多态性﹑封装性 1继承﹐我们每个人都继承了祖先的某些特征﹐公鸡为什么掐着母鸡的脖子?因为她下了一个小象 2多态﹐世界本身就是丰富多彩的﹐没有两个人是一模一样﹐物种都呈现着多样性﹐兄弟都继承父母的特征﹐但是兄弟之间仍然有很多不同的特征 3封装﹐每一个人﹐身体有各种器官﹐还有一些行为﹐你不是散的吧﹐你也和汽车一样是一套东西组成的对不对   我们进一步﹐把这些上升到我们需要的高度﹕ 1继承﹐类之间的内在联系﹐表现为属性和操作的共享﹐目的是代码的重用 2多态﹐用相同的操作名﹐在一个层次不同的类中实现了不同的功能﹐即指允许不同类的对象对同一消息作出响应﹐目的是为了行为的共享 3封装﹐数据和数据的行为作为一个整体包围起来。目的是为了安全的访问一些信息和功能。但是此安全是指程序编写层面的﹐要防止的是错误不是小偷﹗   注意上面的这些很重要﹐我不会在C#中再介绍面向对象了﹐请你自己在讲解中理解   下面就C#而言具体讲一讲类与结构 对象也就是类的一个实例﹐注意我后面如果有这两个词汇请等同成一个概念 类是一种含有数据成员(常量﹑域﹑事件)和函数成员(方法﹑属性﹑索引﹑操作符﹑构造﹑析构)的一种数据结构﹐结构有点象类﹐但是结构是值类型﹐不需要堆栈分配。C#提供的简单类型都是结构﹐象int double bool 1 1.1类声明 [属性选项] [类修饰符选项]  class    [基类选项]  类主体 1.1.1类的修饰符 new public protected internal private abstract sealed new只允许用在嵌套类中(也就是你在一个类中引用另一个类)public protected internal private (protected internal)是说明访问权限的﹐ abstract是修饰抽象类的﹐抽象类只用于派生﹐不同版本组件的开发如果都派生于一个抽象类的话﹐那么它是有兼容性的﹐接口也是这个用途﹐如果你还记得上期接口的概念那么这个抽象类也不难理解了 既然抽象类和接口都在结构上给了我们框架﹐有利于团体编程和跨语言编程﹐那么用哪个好呢?   如果要设计小而简练的功能块,则使用接口。如果要设计大的功能单元,则使用抽象类。 它们都有优点缺点﹐用哪个根据实际需要来确定 sealed是密封的意思﹐密封类是不能被继承的﹐用途就是禁止派生 1.1.2类的基本描述 基类 class A {} class B :A {}   B的直接基类是AA没有指定基类﹐B是派生类 类的基类不可能是System ArraySystem.Delegate,System.Enum,System.ValueType 除了Object类之外﹐其它每个类都有一个直接基类﹐Object是供所有类继承的最终类﹐意思是说,Object是祖宗﹐而且是唯一的 接口﹐可以包括接口类型列表﹐上期讲过了不多讲了                     1.2类的成员 l           常量﹐代表与此类相关联的常数 l           域﹐代表此类中的变量 l           方法﹐可以被类执行的计算和行为 l           属性﹐定义读写相关命名性质和行为 l           事件﹐定义由类产生的停止 l           索引﹐允许类的实例象数组一样检索 l           操作﹐定义可以应用在表达式上的操作 l           实例构造器﹐执行初始化类实例的行为 l           析构器﹐永久清除类的实例行为 l           静态构造器﹐初始化自身的行为 l           类型﹐委托对类来说是局部的类型 1.2.1继承 继承有传递性质 继承类可以扩展它的基类 构造和析构不能被继承﹐ 一个派生类可以通过声明与基类成员相同的名字来覆盖(隐藏)基类的成员 一个类的实例包括这个类和基类中所有域实例的拷贝﹐一个派生类的的实例引用可以看做是指向基类实例的 一个类可以声明虚方法﹑虚属性﹑虚索引﹐而派生类可以重载这些成员的实现﹐这样就具备多态性了 被继承的类就叫基类﹐而继承的类就叫做该基类的派生类﹐C#不允许同层次有多个基类﹐我们下面要用到这些概念﹐请记住 1.2.2new修饰符﹐注意这是成员修饰符 l           abstract﹐说明一个方法或访问标志不能含有实现﹐在派生类中必须提供override l           const﹐应用于局成员或局部变量﹐在编译时候对常量表达式求值﹐因此不能包含变量的引用 l           event﹐定义一个域成员或属性当作类型时间 l           extern﹐告诉编译器方法由外部实现 l           override﹐重载﹐用于基类中定义为virtual的方法和访问标志 l           readonly l           static﹐声明为static的成员属于类﹐而不属于实例 l           virtual﹐说明该方法或访问标志可被派生类重载 1.2.3访问修饰符 l           public﹐任何地方都可以访问该成员﹐具有的限制最少 l           protected﹐在类和所有的派生类中可以访问﹐不允许外部访问 l           private﹐只允许同一个类的内部访问﹐派生类也不能访问 l           internal﹐允许相同组件的所有代码访问﹐就是说在.NET组件级别是public的﹐外部是private 曾经有群友使用自定义控件﹐说里面声明的WebControls不能访问﹐就是声明成了private﹐这又和变量作用域的问题多少相似﹐这个我已经强调了很多次了﹐请一定要注意 1.2.4委托类型﹐也可叫代表﹐这里不讲了﹐以后讲 1.2.5静态和实例成员 类的的成员要么是静态的要么是实例成员﹐通常认为静态成员是属于类的﹐实例成员属于象 当域﹑方法﹑属性﹑事件﹑操作的声明包含了static修饰符﹐就声明了一个静态成员 不管创建多少类的实例﹐静态成员都是相同的地址﹐用同一个成员的拷贝 静态成员不能对特定实例进行操作﹐不能使用this 实例成员与之相反     class Test {        int X;        static int Y; protected void Fun1() {     X=1;//this.X=1是一样的     Y=1; //正确 } static void Fun2() {     X=1;//错误﹐不能访问this.X     Y=1; //正确 } static void Main() {     Test t = new Test();     t.X=1; //正确     t.Y=1;//错误﹐不能通过实例访问静态成员     Test.X=1;//错误﹐不能通过类访问实例成员     Test.Y=1;//正确 } }          1.3常量 常量类型是具有常数值的成员﹐其类型只能是sbyte,byte,short,ushort,int,uint,long,char,float,double,decimail,bool,sting,enum,reference          class Test1          {                    public const double X=1.0, Y=2.0, Z=3.0; }          class Test2          {                    public const double Z=Test1.X*Test1.Y+1.0;//注意常量可以参与常量的声明 }          1.4                    1.4.1静态域和实例域                    static表示声明一个静态域﹐没有就是一个实例域﹐实例域属于具体的对象,为特定的对象所专有。静态域属于类,为所有         对象件所共享                    class Test {       public static int I=1;// 静态域       public int J=2;// 实例域 }                    1.4.2只读域                    class Test {       public static readonly string Black=new string(“#000000”) } 只读域是不是和常量很象?区别是常量在编译阶段就确定了值﹐而只读域的变量只能在运行的时候才能确定 1.4.3域的初始化 一个域的初始值是这个类型的默认值﹐当装载一个类的时候所有静态域被初始化它的类型的初始值﹐当创建一个域的实例时同样实例域也被初始化为初始值          1.5转换操作 它引入了用户自定义转换﹐如果声明了包含implict是自定义转换﹐explict是显示自定义转换﹐转换操作是指对类或者结构类型有关的转换 1.6方法 这是我们最经常使用的﹐修饰符有new,public,protected,internal,private,static,virtual,sealed,override,abstract 其中static,virtual,overideabstract是互斥的         1.6.1方法的参数         共有四种形式参数 l           值﹐没有任何修饰符 l           引用﹐使用ref修饰符 l           输出﹐使用out修饰符 l           数组﹐使用params修饰符 其实这个我在变量的种类已经讲过就不多讲了﹐有点象c中的指针﹐如果不明白请看前面的讲义 1.6.2静态方法和实例方法 静态方法不用实例化类它就可以使用﹐而实例方法则必须实例化才能使用﹐不多讲了 1.6.3 a) virtual方法﹐overide方法 (另外sealed方法也在这里讲) 也就是虚方法和重载方法﹐这两个东西要放在一起讲﹐准确的定义不好理解﹐具体的意思就是﹐在一个基类中声明一个虚方法(注意这个方法是虚的﹐但是它可以执行)﹐在基类的派生类中就可以重载这个方法 sealed方法是必须和 override配对使用的﹐它决定了重载的层次﹐防止派生类向更远的基类重载方法 b) abstract方法 它也是虚方法﹐它的声明已经隐式的声明为了virtual﹐但是与virtual的区别是﹐它的方法里没有实质﹐是抽象的方法﹐派生类必须重载它﹐而对于virtual方法派生类可以选择重载也可以不重载 上面这些修饰符修饰的方法究竟对我们有什么用呢﹐主要是在结构上给了我们巨大的优势﹐比如你要写一个数据库处理的类﹐并从某个基类派生﹐基类的大部分方法都是适用的﹐但某个方法可能对你来说不够强大﹐那么如果基类明这个方法是虚拟的话﹐你可以重载这个方﹐这样你的这个派生类就增强了基础类的功能﹐其它使用者直接使用你这个派生类就可以了﹐注意这就是C#的多态性质﹐和C++的多态不同。 再进一步﹐如果整个类都是abstract的话﹐那么只是给了你一个框架﹐你继承了这个类就继承了这个框架﹐对于我们的大型程序开发和组件开发是大大的有利                    c) extern方法﹐里面没有C#的语句﹐只引入外部函数 class path {        [DllImport(“kernel32”,SetLastError=true)]        static extern bool CreateDirectory(string name,SecurityAttributes sa); }            1.7属性 修饰符有new,public,protected,internal,private,static,virtual,sealed,override,abstract其中static,virtual,overideabstract是互斥的 和方法一样也有静态属性和实例属性﹐这里不多讲了 主要讲讲访问标志﹐根据有没有getset﹐分为只读﹐只写﹐和可读可写 属性比较好理解﹐不多讲了举个例子 class Test {         private string _name;         publci string name         {                 get {return _name;}//Test有一个只读的属性name } } 1.8事件﹐不讲了 1.9索引 也不深入讲了﹐有了它可以像访问数组一样的去访问类中的值 1.10构造器(实例构造器和静态构造器) 和实例域﹑静态域一样﹐一个属于类的实例﹐一个属于类 a)实例构造 是指初始化一个类实例的行为﹐主要初始化类中的实例变量﹐只有在用户用new关键词为对象分配内存时才被调用 Object外其它所有的构造都有初始化 举个例子 class Test {         int x =1, y = -1 ,count;         public Test()         {                 count = 10; }         public Test(int n)         {                 count = n; } } 我们定义了两个构造器﹐一个有参数﹐一个没有参数﹐当我们实例Test时就可以 public Test a = new Test()或者是public Test a = newTest(100) 如果有B类基于A的话 那么B的构造可以直接从基类中调用 class A { public A(int x,int y){} } class B:A {         public B(int x, int y): base(x+1,y+1) {} } 也可以通过this(而不是base)从类的自身调用 class Test { public Test () : this (0, null) {}
public Test (int x, object o) {}
} 如果类没有声明一个构造器那么系统会自动提供一个默认的构造器﹐现在你写ASP.NET大部分都不自己设计构造器吧﹐系统都会给你安排一个﹐但是要注意必须符合以下条件,子类没有声明任何构造器﹐编译器为子类加的默认构造器一定为无参数的构造器﹐父类一定要存在一个无参数的构造器。 注意如果我们声明构造时如果是private那么它是不能被派生类继承的   这里也讲下析构器﹐它是消除一个类实例的行为﹐析构器器只负责回收处理那些非系统的资源﹐如打开的文件,句柄,数据库连接,网络连接等等需要用户自己动手释放的非内存资源 析构器的命名采用C++的命名约定﹐使用~符号﹐析构器只能应用于引用类型﹐并且不可被重载﹐析构器不可被显式调用﹐在对象所占用的内存被回收前,对象继承层次里的每一个析构器都会被调用 class A { public A(int x,int y){} ~A(){} }   b)静态构造器 它是初始化类的行为﹐主要初始化类中的静态变量﹐它不能被用户直接调用﹐也就是不能明确调用﹐如果一个类有静态构造器那么加载类的时候就会自动调用 class Test { static int[] myArray = new int [100];
      static Test ()
{ for (int i = 0; i < myArray.Length; i++)
myArray [i] = i;        
}          } 在一个程序的执行过程中,静态构造器最多只执行一次﹐静态构造器在类的静态成员初始化之后执行﹐或者讲编译器会将静态成员初始化语句转换成赋值语句放在静态构造器执行的最开始﹐静态构造器在任何类的静态成员被引用之前执行﹐静态构造器在任何类的实例变量被分配之前执行 2结构 结构我就不深入讲了﹐结构跟类很相似﹐也比较好理解﹐注意结构和类的区别﹐可以把它看做是轻量级别的类 结构是值类型﹐类是引用类型 结构不能继承 结构不分配堆栈 结构的默认值不会是null 结构的装箱和拆箱与类明显不同﹐结构是对值的拷贝进入或推出装箱实例 没有域初始化 没用构造器 没有析构