Essential.C#第五章 类

摘要:
作为一种面向对象的语言,C#完全支持类和构造对象。例如,OpticalStorageMedia类型有一个方法Eject()从播放器中弹出CD/DVD。opticalStorageMedia类是现实世界的编程抽象。这是一系列isa关系。但是,CD和DVD不需要直接来自StorageMedia。它们源自中间类型OpticalStorageMedia。classProgram{classEmployee{//…}staticvoidMain(){Employereemployee1,employee2;}静态void IncreaseSalary{//…}}类声明下的花括号标记了执行边界。C#使用new关键字类实例化一个列有5.3 classProgram{classEmployee{//…}staticvoidMain()StaticvoidIncreaseSalary{//…}}不要惊讶,赋值可以与声明语句位于同一行,也可以单独操作。这些卡片一文不值。提供了一组类成员,以便不再单独处理它们。

在第一章中你已经看到了如何声明一个新类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类的实例中共享数据。

扩展方法

免责声明:文章转载自《Essential.C#第五章 类》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇2020 第九届全国生物信息学与系统生物学学术大会Ubuntu18.04+GTX1080Ti+CUDA9.0+cuDNN7.0+TensorFlow-GPU1.9环境搭建下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

iOS学习——iOS常用的存储方式

不管是在iOS还是Android开发过程中,我们都经常性地需要存储一些状态和数据,比如用户对于App的相关设置、需要在本地缓存的数据等等。根据要存储的的数据的大小、存储性质以及存储类型,在iOS和Android中哪个都有多种存储方式。其中,iOS中的存储方式主要包括以下六类: plist文件(属性列表) preference(偏好设置) NSKeyedA...

【Java面向对象 05】

一、构造方法 1、方法名与类名称相同,2、没有返回值,不需要void关键字2、调用类里面的属性必须先进行对象的引用,即:new 类名(),引用:引用名.属性以下举例:Test01类对Students类进行初始化调用,Test02类对Students类进行属性的修改,即修改成员变量 public class Students { //学号 i...

C#打印机操作类

using System; using System.Collections.Generic; using System.Text; namespace MacPrinter { public class ZPL_Command { /// (^EF)<summary> /// 清除设定 (已...

C#枚举(一)使用总结以及扩展类分享

0.介绍 枚举是一组命名常量,其基础类型为任意整型。 如果没有显式声明基础类型, 则为Int32 在实际开发过程中,枚举的使用可以让代码更加清晰且优雅。 最近在对枚举的使用进行了一些总结与整理,也发现了一些很有意思的知识盲区。 接下来先简单为大家介绍枚举在开发过程中的常用内容以及扩展类的分享。如果喜欢直接看代码的可以查看最后的样例源码。 1. 参考资料 官...

ADO.NET基础必备之SqlParameterCollection 类

SqlParameterCollection 类 表示与 SqlCommand 相关联的参数的集合以及各个参数到 DataSet 中列的映射。无法继承此类。 语法:  [ListBindableAttribute(false)] public sealed class SqlParameterCollection : DbParameterCollecti...

iOS数据持久化的方式

概论 所谓的持久化,就是将数据保存到硬盘中,使得在应用程序或机器重启后可以继续访问之前保存的数据。在iOS开发中,有很多数据持久化的方案,接下来我将尝试着介绍一下5种方案: plist文件(属性列表) preference(偏好设置) NSKeyedArchiver(归档) SQLite 3 CoreData 沙盒 在介绍各种存储方法之前,有必要说明以...