zoukankan      html  css  js  c++  java
  • Essential.C#第五章 类

        在第一章中你已经看到了如何声明一个新类Helloworld。在第二章,你也也学习了C#内建的原类型。你也知道了控制流,还有如何声明方法。现在是时候讨论如何定义属于自己的类型了。这在任何C#程序都是核心结构。C#作为一个面向对象的语言是完全支持类和构建对象。

    image

        本章为你引入c#的面向对象编程方式。重点是如何定义类,这是对象的模板。

        在前面的章节的程序结构一直都是面向对象的编程。不管怎样,用来包裹这些构造,你就能创建更大,更有组织的程序,并具有可维护性。从结构化,控制流程编程过渡到面向对象编程是革命性的变革。因为这提供了额外的组织形式。这带来的结果就是,小程序变的有点简单,但更重要的,这将能创建更大的程序,因为编程代码被更好的组织起来。

        面向对象编程还有一个关键的特性是,避免从零编写一个新程序,你能收集从前工作中的对象,并扩展类的新特性,添加更多类,然后用新功能重组。

        本章深入讨论C#是如何支持封装的。比如,类型,属性,还有访问修饰符。下一章在本章基础上,介绍继承和多态。

    面向对象的编程

        今天程序能成功的关键是,在越来越大的应用中用结构化的方式实施复杂的需求。面向对象的编程就提供了一套实现方法。预想将面向对象的程序转换成结构程序是很困难的。除了某些无意义简单的程序。

        面向对象编程最基本的结构式类和对象。这是对现实世界的模块、模板概念的在编程中的抽象形式。比如,类型OpticalStorageMedia有一个方法Eject()用于从播放器中弹出CD/DVD。类OpticalStorageMedia就是对真实世界的程序化的抽象。

        类依据三个面向对象的原则:封装,继承和多态。

    封装

        封装是将细节隐藏,当需要时就可以访问这些细节,对细节的合理封装,很容易让大程序理解。无意中修改被保护的数据,由于改变代码是在封装的边界内,代码就可以很容易保持。方法就是封装的例证。尽管可以将方法中的代码直接嵌入调用者代码中,不过,将代码重构编入方法提供了有益的封装。

      

    继承

        看看下面的例子,DVD是光学媒体的一种。它可以存储数字影片。CD是另一种有不同性质的光学媒体。它们两者的版权实现是不同的,并且存储容量也不同。不同的存储介质,都有特定的特性,不过都会有些基本功能,比如,支持文件系统,媒体文件是只读还是可写。

        如果你为每个存储媒体定义一个类型。你就要定义一个类继承。这是一系列的is a关系。基础类是所有存储介质驱动器的源头。它可以是StorageMedia类。比如,CD,DVD,硬盘,软驱,等等都是StorageMedia类型。然而,CD和DVD并需要直接源自StorageMedia。而它们是源自一个中间类型,OpticalStorageMedia。你可以用UML来查看类继承图。

    image

        继承关系需要至少两个类,其中一个是更普遍的版本。在图5.1中,硬盘是更具体的版本,尽管HardDrive有许多特殊类型,它依然是StorageMedia。反之则说不通。StorageMedia类型不一定是hardDrive类型。图5.1中的继承关系中就包含了多个类。

        特定类型是衍生类型或叫子类。而通用的类型叫基类或超类。在继承关系中类的另一种较常用的术语是父亲和子孙。前者是更通用的类。

        从另一个类衍生或继承到特定的类型,是指自定义基类以便能面向特定用途。同理,基类是衍生类的通用实现。

        继承的关键是,所有衍生类集成基类的成员。通常,基类的成员实现能够被修改。衍生类能包含基类的成员。

        衍生类允许你以层级的关系组织你的类,孩子类会有比父亲类更多的特性。

    多态

        多态包含两个意思,一个是“多”另一个是“形式”。在对象的上下文中,多态的意思就是一个简单的方法或类型能有多种实现。如果你想有一个媒体播放器。它能播放音乐CD,DVD还有MP3.然而,这就需要依赖不同媒体类型精确实现Play()方法。在CD对象上调用Play()或在DVD播放,这都会播放音乐。所有的类型都知道其基类,OpticalStorageMedia,并都有各自的方法声明。多态是一个规范,类型能处理方法实现的细节,因为,在多个衍生类中都有方法出现,它们都共享基类中的同样的方法声明。

    定义和类实例

         定义一个类,首先用关键字class,后跟标示符。

    清单5.1
    ————————————————————————
    class Employee
    {
    }
    ————————————————————————

    类中的代码都要包含在类声明之后的花扩号中。虽然这不是必须做的,但通常将类放入各自的文件中。这样就能很容易的找到特定类。习惯上,类名就是文件名。

        一旦你定义了一个新类,你就能将它构建入framework中。换句话说,你能声明一个此类型的变量,或将这个新类作方法参数传递。

    class Program
    {
        class Employee
        { 
            //...
        }
     
        static void Main()
        {
            Employee employee1, employee2;
        }
     
     
        static void IncreaseSalary(Employee employee)
        {
            // ...
        }
    }

    类声明下面的花括号划分出执行界限。

    定义对象和类

        在非正式的讨论中,对象和类是可以混用的。然而,对象和类却有着不同的意思。类是一个对象在运行期的模板。对象是一个类的实例。类就像一个模具,将会创建一个部件。对象就是模具对于的部件。从类创建对象的过程叫实例化,因为对象是类的实例。

        你已经定义了一个新类,现在是实例化这个类型的时候了。C#使用new关键字类实例化一个对象

    清单5.3

    class Program
    {
        class Employee
        {
            //...
        }
     
        static void Main()
        {
            Employee employee1 = new Employee();
            Employee employee2;
            employee2 = new Employee();
            IncreaseSalary(employee1);
        }
     
        static void IncreaseSalary(Employee employee)
        {
            // ...    
        }
    }

    不要吃惊,赋值能和声明语句在同一行,也可以分开操作。

        不像原始类型,并没有直接指定Employee。代替的操作是,用new操作符来在运行时为Employee对象分配内存,实例化这个对象,并返回此实例的引用。

        尽管分配内存是显式的,但并没有相应的回收内存的操作符。在程序结束以前,自动回收对象内存。垃圾回收器负责内存自动分配。由它确定没有被活动对象引用的对象,然后重新将内存分配给别的对象。内存会被系统重新分配,它并不是在编译时定位。

    封装的第一季:用方法分组对象数据。

        如果你收到用员工第一个名字做索引的卡片,用员工第二个名字做索引的卡片,还有一个用他们薪水做索引的卡片。这些卡片没什么价值,unless you knew that the cards were in order in each stack。所以,为了得到员工的全名就需要搜索两个卡片。

        在非面向对象的语言上下文中,将一组项目装入一个封套中,同样面向对象编程将方法和数据一起装入对象。提供了一组类成员以便不再单独处理。

     

    实例化字段

        面向对象设计的一个关键目的就是将数据分组。本节讨论如何将数据加入类。在面向对象的术语中,类中存储数据的变量叫做成员变量,此术语仅对C#有效。更为精确的术语是字段,用内置类型为存储数据的单元命名。实例化字段是在类层次为对象要存储的数据声明一个变量。因此,联合就是字段类型和字段之间的关系。

     

    声明一个实例字段

        在清单5.4中,Employee已经被修改成包含三个字段的类:FirstName,LastName,Salary

    清单5.4

    class Employee
    {
        public string FirstName;
        public string LastName;
        public string Salary;
    }

    对于增加的这些字段,employee类的实例可以存储一些基本数据。所以这些字段都用了public访问修饰符。字段上的public指示了除了employee以外别的类也可以访问这些字段。

         和本地变量声明一样,一个字段的声明包含和字段相关的数据类型。此外,在声明期,会给字段分配一个初始值。

    清单5.5

    class Employee
    {
        public string FirstName;
        public string LastName;
        public string Salary = "Not enough";
    }

    访问一个实例字段   
        你可以设置或检索字段内的数据。然而,字段是不能包含static修饰符的。你能从对象上访问一个实例字段,而不能通过类直接访问。

       

    实例方法

        在类中提供了一个处理员工姓名格式化的方法,来替代main()方法中的WriteLIne()方法。这样做是符合类封装性的。

    使用this关键字

        你能获得类的实例成员引用的类。为了明确指示能访问的字段或方法。你需要使用this、this是一个简单内含字段,它返回对象本身。

    class Employee
    {
        public string FirstName;
        public string LastName;
        public string Salary;
        public string GetName()
        {
            return FirstName + " " + LastName;
        }
        public void SetName(string newFirstName, string newLastName)
        {
            this.FirstName = newFirstName;
            this.LastName = newLastName;
        }
    }
     

       

    用代码格式避免歧义

        在SetName()方法中,你不用使用this关键字,因为FirstName和newFirstName是显然不一样的。考虑另外一种情况,如果用FirstName代替newFirstName

    清单5.10

    class Employee
    {
        public string FirstName;
        public string LastName;
        public string Salary;
        public string GetName()
        {
            return FirstName + " " + LastName;
        }
        // Caution:  Parameter names use Pascal case
        public void SetName(string FirstName, string LastName)
        {
            this.FirstName = FirstName;
            this.LastName = LastName;
        }
    }

    文件存储和载入

     

     

    封装第二季:信息隐藏

        除了将数据和方法一起封装入一个简单的单元。封装还可以隐藏一个对象的数据和行为的内部细节。在一定程度上,方法也是做这个事。方法声明是对所有调用者可见的。而内部实现是隐藏的。面向对象的编程也具有这个特性,然而,它提供了一个工具用来控制成员在类外部的现实范围。不将成员公开给类外部是私有成员。

        在面向对象的编程中,封装不仅仅是分组数据和行为的术语,它还可以将数据隐藏在类内部以便最小限度的曝光类的访问。

        访问修饰符的作用就是封装。使用public,你明确的指示了可以从Employee类的外部修改字段。换句话说,可以从Program类访问Employee类。

        为了将Password字段隐藏,不让别的类触及到。你要使用private访问修饰符。这样在Program类中你就不能访问到Password字段了。

        注意当没有为成员指定访问修饰符,默认的就是private。换句话说,成员默认是私有的。程序员需要明确指定成员为公有。

    属性

        在上一节,访问修饰符,演示了,如何使用private关键字封装一个密码,阻止从类的外部访问。这样的封装太彻底了。比如,有时你想定义一个字段,外部类只能读它,并不能改变它的值。还有,你可能要向类中写入某些数据,但你想验证数据。有太多的例子需要即时创建数据。

        通常,编程语言会有这样的特性,将一个字段值为私有,然后为访问和修改数据分别提供getter和setter方法。

    清单5.16

    class Employee
    {
        private string FirstName;
        // FirstName getter
        public string GetFirstName()
        {
            return FirstName;
        }
        // FirstName setter
        public void SetFirstName(string newFirstName)
        {
            if (newFirstName != null && newFirstName != "")
            {
                FirstName = newFirstName;
            }
        }
        private string LastName;
        // LastName getter
        public string GetLastName()
        {
            return LastName;
        }
        // LastName setter
        public void SetLastName(string newLastName)
        {
            if (newLastName != null && newLastName != "")
            {
                LastName = newLastName;
            }
        }
        // ...
    }
     

    这一改变,会影响编程的方式,你不再需要使用赋值操作符设置数据。

    声明属性

        C#为这种模式提供了实现语法。这种语法叫做属性

    自动实现属性

        C#3.0中,属性语法中包含一个精简版本。

    命名习惯

     

    用属性校验

    只读属性和只写属性

     

    在Get和Set上的访问修饰符

    属性作为虚字段

    静态

    静态字段

        为了定义一个在多个实例中都可以访问到的数据,需要使用static关键字。

    在这个例子中,NextId字段声明包含static修饰符,所以这就叫做静态字段。而Id就是一个单纯存储位置。NextId在Employee类的实例中共享数据。

    扩展方法

  • 相关阅读:
    null和undefined的区别
    "NetworkError: 404 Not Found fontawesome-webfont.woff?v=4.0.3
    php字符串
    php数组
    Oracle 和 MySQL的区别(不完整)
    拦截器和过滤器的区别
    SpringMVC和Struts2的区别
    Redis的介绍
    SpringBoot入门(2)
    SpringBoot入门(1)
  • 原文地址:https://www.cnblogs.com/yaoshi641/p/1560174.html
Copyright © 2011-2022 走看看