C#成神之路<13> C#对类的设计

来源:互联网 发布:lifemod软件下载 编辑:程序博客网 时间:2024/05/16 23:51

1、向项目中添加类
现在已经准备好向该项目添加一个新类
Project->Add Class添加新类,并且进行命名。
之前建立的代码frmMain是窗体文件,而clsDates是一个类似于纸上的c#徽标。这是强调向项目中添加一个类,而不是添加一个新的Windows窗体。并且在项目中添加了两个引用。System.XML和System.Data。

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace 类设计实例{    class clsDate    {    }}

新建类系统自建代码如上。
在以上代码的基础上进行修改,得到:

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace ClassDesign {    class clsDate    {        int scopeClass;        public int MyFunction()        {            int scopeLocal;            for (int scopeBlock = 0; scopeBlock < 20; scopeBlock++)            {             }        }    }}

下面将会依据上面的程序对一些重要的概念进行剖析。

2、作用域
作用域是指变量的可见性和声明周期。只要某个变量再作用域中,就具有对该变量的访问权限,并且可以在代码中使用它。如果某个变量再作用域之外,那么就相当于该变量不存在。

(1)块作用域

for (int scopeBlock = 0; scopeBlock < 20; scopeBlock++)            {             }

如上面代码中的一部分,变量的作用域从变量的定一点延伸到定义它的代码块的右大括号处。

(2)局部作用域
局部作用域是在方法块内部和语句块外部定义的(也成为方法作用域)。局部作用域从变量定义位置延伸到定义变量的方法的右大括号。

(3)类作用域
类作用域的变量是在类的内部和方法的外部定义的变量。(也称之为模块作用域)类作用域的范围从变量定义的位置扩展到该类的右大括号。

int scopeClass;

以上的变量是作为类作用域变量的示例。
类作用域同时包括了方法和语句块作用域。意味着有类作用域的变量可以同时在方法级和语句块级中使用。换个角度思考:类作用域的变量就是类的属性。类的方法始终有类作用域。

(4)名称空间作用域
名称空间作用域适用于在当前名称空间中定义的任何变量。
命名空间作用域包含当前项目中所有程序元素(类、方法、变量)并且用于区分采用相同名称的程序元素。

namespace ClassDesign 

如上,这个语句指出,当前开发的项目名称为classdesign。意味着可以按照如下代码引入程序的入口点:

ClassDesign.frmMain.Main();

使用命名空间作用域的原因:防止该命名空间当中已有的类和方法产生名称冲突。如果两个程序中都有clsDatabase类,则可以应用正确的名称空间使用来自于所需项目的正确的类。

使用作用域的原因(程序思想)
这种对作用域的支持是尝试尽量减少程序中数据项之间产生的不需要的交互。
由于变量作用域加宽时,需要考虑的代码也越来越多,所以,程序设计应该尽可能使用对现有任务有意义的最窄作用域级的数据。这种隔离程序中数据的过程叫做封装。

3、设计程序
对于程序设计的程序思想的描述,将会在此次程序设计中详细介绍。
充分利用五步编程方法完善程序的编译。

程序功能:能够确定复活节日期的程序,并且表明正在讨论的年份是不是闰年。
解决方法:五步编程方法。

(1)初始化
恰当的初始化放在frmMain窗体上的对象。并将该窗体显示在屏幕上。

using System;using System.Windows.Forms;public class frmMain : Form{    private TextBox txtYear;    private Button btnCalc;    private Button btnClose;    private Label lblLeapYearResult;    private Label lblEasterResult;    private Label label1;    #region Windows code    private void InitializeComponent()    {    }    #endregion    //#region是C# 预处理器指令。     //#region 使您可以在使用 Visual Studio    //代码编辑器的大纲显示功能时指定可展开或折叠的代码块。    public frmMain()    {        InitializeComponent();    }    public static void Main()    {        frmMain main = new frmMain();        Application.Run(main);    }}

当程序开始执行的时候,已经知道是用户名为Main()方法开始执行。在Main()语句中:

 frmMain main = new frmMain();

表明程序做的第一件事:创建frmMain类的对象的一个实例。并且将它命名为main。
而frmMain的构造函数每一次都调用InitializeComponent()。InitializeComponent()方法是使windows重新构建在拖放对象到空窗体上时构建的窗体。
一旦内存中构建了窗体,控制返回main函数,并且执行:

Application.Run(main);

Windows显示frmmain类的窗体当前存储在内存中的映像。

(2)输入步骤
用户需要输入年份并且单击calculate。然后程序控制进入了btnCalc的事件方法中。

(3)处理步骤
程序调用一个方法,获取用户输入的年份,并且确定复活节的日期。程序还需要一个方法来确定那一年是否为闰年。

(4)显示步骤
在处理步骤的信息填充到两个标签对象的text属性上。

(5)终止步骤
直接调用close方法结束程序。

总结:
处理步骤之外的所有步骤都与frmMain类有关。处理步骤之外的所有步骤都与窗体frmMain上显示的可视化组件交互。而处理步骤与frmMain窗体可视化或者对象是没有直接关系的。这意味着可以编写完全将其与可视化隔离开来。使之成为一个功能独立的模块。从而实现OOP代码重用的巨大优势。

4、UML Light
UML:建模对象的标准化规范语言。
此处应用UML的子集,这些子集称之为UML类图,是组成类的部件的可视化表示。
UML类图可以看做三个叠加起来的方框。最上面的方框存放类的名称,类名方框下面是描述类的属性的方框(类的成员和类的属性)第三个方框描述该类支持的方法。UML最新版本支持第四个方框:类的持久保存方式。(永久存储在磁盘数据文件或者数据库的方式)

(1)访问说明符:

-yearint 

-:表示这个特定属性的访问说明符。
private:只能用属性方法从类的外部访问该类。
(-是private的UML符号)
public:意味着属性可以直接访问,而不需要使用该对象的属性方法。
(+是public 的UML符号)

I.访问说明符和作用域
private说明符可以强化封装数据的OOP设计目标。

II.static关键字

-daysInMonth:static int[]

翻译:daysInMonth是使用static存储类的private整数数组。
static表示一些信息,但最重要的信息无论在程序中创建该类多少实例,都只能得到一个数组。
使用static当中所有数据可以安全地在类的实例之间共享,或者在程序准备开始执行时必须提供的数据之间的共享。

(2)UML方法

+getLeapYear(year):int

翻译:getLeapYear方法是一个public方法,传递给它year并且返回int数据类型值的参数。

I.public方法
大多数类方法被设计用来对一块或者多块数据执行特定的任务。这些数据块往往是类属性。
类中赋予public访问符的所有元素都成为该类的用户界面的一部分。
类API:使用public访问说明符的类的所有元素。(外部世界和类交互的唯一方式就是通过其public属性和方法)

private方法
private方法不能使类API的一部分。如果方法是private那么,他就具备类作用域,并且在定义它的类外部不可见。

private方法称之为辅助方法:Helper。

II.类属性和方法的名称
当设计类时,在命名属性和方法方面有很大自由。显然不能使用C#关键字作为名称但是除此之外可以使用任意的名称。(命名类的规则和命名变量的规则相同)
属性名采用小写字母,名称作为其功能的提示
对API方法使用操作名称,API方法名应该与现实无关

5、cls类设计
以下为上述设计理念所体现的编程:
(这里实现对闰年的判断以及计算复活节的日期)

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;public class clsDates {    private static int[] daysInMonth = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };    private int day;    private int month;    private int year;    private int leapYear;    private DateTime current;    public clsDates() {        current = DateTime.Now;    }    public int getLeapYear()     {        if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0)            return 1;        else            return 0;    }    public string getEaster(int year)    {        int offset;        int leap;        int day;        int temp1;        int temp2;        int total;        offset = year % 19;        leap = year % 4;        day = year % 7;        temp1=(19*offset+24)%30;        temp2 = (2 * leap + 4 * day + 6 * temp1 + 5) % 7;        total=(22+temp1+temp2);        if (total > 31)        {            month = 4;            day = total - 31;        }        else {            month = 3;            day = total;        }        DateTime myDT = new DateTime(year,month,day);        return myDT.ToLongDateString();    }}

以上代码删除了namespace的声明,删除操作的原因是希望clsDates类可以在可能开发的其他程序中使用。而不必使用项目名称空间作为类层次结构的一部分。
下面详细剖析这个类的组织结构:
符号常量、类的static成员、实例成员、构造函数、属性方法、辅助方法、一般方法。
static数据是在程序加载的时候创建的。(即使程序永远都不会到达定义clsDates对象的位置,daysInMonth数组仍然会存在于内存中。)
实例对象:所有非static属性的通用术语。
一般方法:成为类的API的一部分的方法。所以一般方法都是用public访问说明符写的。
方法头:表述方法的目的、传递给他的数据的参数列表,以及返回的数据类型。
可以采用这种结构,用它可以进行即时创建文档。(编写一个读取存储磁盘的程序)

6、窗体类设计

using System;using System.Windows.Forms;public class frmMain : Form{    private TextBox txtYear;    private Button btnCalc;    private Button btnClose;    private Label lblLeapYearResult;    private Label lblEasterResult;    private Label label1;    #region Windows code    private void InitializeComponent()    {    }    #endregion    //#region是C# 预处理器指令。     //#region 使您可以在使用 Visual Studio    //代码编辑器的大纲显示功能时指定可展开或折叠的代码块。    public frmMain()    {        InitializeComponent();    }    public static void Main()    {        frmMain main = new frmMain();        Application.Run(main);    }    private void btnCalc_Click(object sender, EventArgs e)    {        bool flag;        int year;        int leap;        clsDates myDate = new clsDates();        flag = int.TryParse(txtYear.Text, out year);        if (flag == false)        {            MessageBox.Show("digit characters only in yyyy format", "input error", MessageBoxButtons.OK, MessageBoxIcon.Stop);            txtYear.Focus();            return;        }        leap = myDate.getLeapYear(year);        lblLeapYearResult.Text = year.ToString() + "is" + ((leap == 1) ? " " : " not ") + "a leap year";        lblEasterResult.Text = myDate.getEaster(year);    }    private void btnClose_Click(object sender, EventArgs e)    {        Close();    }}

注意:程序员经常根据是否为闰年来计算二月份的天数,如果使得判断闰年的程序返回的是布尔值就很难快速计算出二月份的天数。

     lblLeapYearResult.Text = year.ToString() + "is" + ((leap == 1) ? " " : " not ") + "a leap year";

注意这种巧妙的设计方式。
这是一个SOC示例,使用三元运算符来确定消息字符串的构建方式。

6、用户界面的不同含义
(1)多数人认为用户界面是终端用户与其交互的程序的一部分。这种用户界面在窗体上呈现可视化对象。比如最终使用frmMain。这种情况下,用户界面作为获得输入和将结果显示给终端用户的主要方式。

(2)第二种用户界面根本不需要可视化表示。所有具有public访问说明符的属性和方法组成类的用户界面。此刻的用户实际上是程序员。类的public属性和方法形成该类的API,因此定义了程序员与之交互的方式。

0 0