志在指尖
用双手敲打未来

C#基于对象的程序设计(一)

C#基于对象的程序设计(1)

教学目标:面向对象编程是当前计算机界关心的重点,是软件开发方法的主流。本章从面向对象的基本概念入手,详细介绍面向对象编程中的知识点。

教学重点:面向对象编程(OOP)是将现实中的事物抽象化,其程序设计的重点就是类的设计。理解和掌握OOP开发中的一些基本术语。

教学难点:1.类和对象,对象声明和对象创建

2.区分字段和属性

3.静态成员和实例成员以及它们访问方式的区别

4.方法的重载

C#基于对象的程序设计

C#基于对象的程序设计

1..NET概述

“对象”这个概念的出现,是程序设计领域的一次重大进步。虽然从传统的过程式程序设计转移到基于对象和面向对象的程序需要一个比较困难的过渡,但是一旦习惯于使用对象,恐怕就没有办法想象在没有对象的时候,程序是怎么设计出来的。在生活中,我们所指的对象就是“东西”,几乎任何东西都可以看成是对象。把一个东西看成对象,我们就可以孤立的去审查它的性质,它的行为,进而研究它和其他对象的关系。同样,在程序中使用对象的概念,就让我们可以把一个程序看成事很多对象相互作用的结果。这个思想其实很早就应用于人类生产了。例如看电视,我们通过遥控器就能控制它。并不需要知道电视是怎么搜索频道、接收信号等工作,按下按钮,就能换到另一频道。我们只是把电视当作一个供我们娱乐的对象,它就像一个“黑盒子”。我们只要知道怎么用就行了。这实际上是面向对象的第一个重要特点:封装。电视机从出现到不断发展,从黑白到彩色,到高清晰,不管怎么变化,而声音、图像这些功能一直延续下来,这在面向对象理论中就是“继承”,后一代继续前一代的功能并有所发展。而老的录像机除了能与80年代的电视机一起很好的工作,甚至能接到最新的电视机上工作,为什么呢?因为电视机的音频和视频输入\输出端子仍然没有变化。也就是说,电视机这个对象的对外“接口”是稳定的。接口的稳定保证了对象在各自发展的同时,还可以进行交互。通过电视机的例子,面向对象的基本思想就基本成型了。C#是一种面向对象的语言,所以只要一开始c#的编程,就会用到对象。我们通过程序来说明:

using system

namespace _01_01

{

       class Class01

       {

              // 应用程序的主入口点。

              static void Main(string[] args)

              {

                     System.Console.WriteLine("Hello, world!");

              }

       }

}

程序中console就是一个对象,它代表了系统的控制台,WriteLine是这个对象的一个方法,它把后面的“Hello,World!”写到控制台的输出。尽管这个程序的执行过程会涉及到一系列的诸如处理缓冲区,调用中断、显示器控制等等,但是普通程序员不需要知道这些,就像我们看电视不需要知道电视机的工作原理一样。

2.类

程序中还定义了一个类class01,它包含了程序的入口,也就是main函数。初学者往往混淆类和对象的概念,实际上它们之间是“蓝图”和“产品”的关系。类就相当于一张蓝图,它规定了对象具有哪些特征,而对象是根据这张蓝图生产出来的产品。从同一个类产生出来的对象也是一样,它们具有同样的结构和规格,却可能在某些特性上有不同之处。

在c#中定义一个类的方法就时使用class关键字,在其后跟上的是类的名字。例如要定义一个轿车类{Car}。

  • 如果要产生这个类的对象,就要使用new关键字
  • 从一个类产生的每一个对象叫做这个类的实例。
  • 把类名放在前面,后面跟上对象的名字产生的是一个引用,而new关键字会产生一个类的实例。

Car Car1;//只不过声明了一个空引用,不指向任何实际的实例

Car1=new Car();//car1指向了一个car的实例,过程如下:

Car Car2=new Car();

整个过程也可以写在同一个语句中,就像car2那样。

  • c#中的命名惯例时类名的第一个字母大写
  • 如果类名有多个单词,则每个单词的第一个字母大写。

例如:一个警车类:class PoliceCar{……}

3.类的字段

一个空的类是没什么用的,类的作用是描述对象的结构。为此,类必须要有成员。例如对于一辆轿车,我们可能会关心它的颜色,重量和出厂日期。我们可以为Car类加上三个成员:

class Car

{

   string color;

   float weight;

   DataTime releasedata;

}

我们把这种在类中用来存储信息的成员又称为“字段”。除了指明字段的类型外,比较重要的是可访问性级别,它用访问修饰符来表达。

声明的可访问性 意义
public 访问不受限制
protected 访问仅限于包含类或从包含类派生的类型
internal 访问仅限于当前程序集
protected internal 访问仅限于包含类派生的当前程序集或类型
private 访问仅限于包含类型

现在用得最多的访问修饰符主要是public和private。其余要等到学习了继承和程序集的概念后才用到。所谓public就是说任何人都可以访问这个字段(公用电话),而private则刚好相反,仅在同一个类中可以访问(日记本)。

private是默认的修饰符,前面的Car类中的三个字段都没有加上访问修饰符,所以都是private的。这个程序说明了访问修饰符的作用,在编译时会出错,因为程序在类的外部访问了类的private字段。

using System;
namespace _03_01

{
       class Car
       {

              public string color;

              private float weight;

              private DateTime releasedate;

       }
       class Class_03_01
       {

              public static void Main(String[] args)

              {

                     Car car1 = new Car();

                     car1.color = "Red";

                     car1.weight = 1.1;  // 出错!因为weight是private成员

              }

       }

}

静态字段:

  • 如果在一个类字段之前加上static修饰符,就可以将它声明为一个静态字段
  • 静态字段属于类所有,所有的类实例都共享这个静态字段。

与之相比,前面声明的都是非静态字段(实例字段),每个实例都拥有自己的实例字段。静态字段通常用来存储一些属于全部实例的信息,例如对于Car类的实例,虽然每个Car实例都有自己的颜色、重量等,但是如果需要存储所有Car实例的数量,就需要用到静态字段。现在,几个Car对象的关系如图所示:

class Car

{

 string color;

float weight;

DataTime releasedate;

public static int count;//静态字段,记录实例的数量

}

Car car1=new Car()

Car car2=new Car();

Car car3=new Car();

由此可见,不管有多少个Car对象,变量count都只有一个。实际上,如果要访问静态字段,不能用对象的名称,而要用类名。例如,访问实例字段color所用的语句是:

Car1.color=”Red”; Car2.color=”Blue”;

而访问count的语是:Car.count=Car.count+1;

4.类的方法

车不光要有颜色,重量等特征,还会有一些动作,例如行驶。如果要表达一个对象的动作,就应该类中设计相应的方法。和字段一样,方法也是类成员的一种。下面的代码为Car类加上了行驶(run)方法。

class Car

{

string color;

……

public static int count;

public void Run()//一个方法

{ console.WriteLine(“我正在路上行驶……”);}

}

如果说普通的字段时存储信息的话,方法就时“做事情”的成员。代码中的Run方法会在控制台输出一句话。我们可以用这个程序来验证。

using System;
namespace _03_02
{
       class Car
       {
              public string color;

              private float weight;

              private DateTime releasedate;

              public static int count;

              public void Run()

              {

                     Console.WriteLine("我正在路上行驶……");

              }

       }
       class Class_03_02

       {

              public static void Main(String[] args)

              {

                     Car car1 = new Car();

                     Car car2 = new Car();

                     car1.Run();

                     car2.Run();

              }

       }

}

这个程序在编译的时候会产生一些警告信息,先不用理会。程序产生了两个Car对象,并且分别对它们调用了Run方法,结果如我们所料,输出了两行字。

  • 和普通的成员一样,方法可以用访问修饰符来修饰,表达的意义也一样。
  • Public方法是任何人都可以调用,而private方法只在类的内部可以调用。
  • 由于方法是位于类的内部,所以它总可以访问类中的成员,包括private成员。

如果方法老做同一件事就没意思了。怎么办呢?方法可以接受参数,实际上是与外界的一种“通讯”方式。我们看这个程序:

using System;
namespace _03_03
{

       class Car
       {
              public string color;

              private float weight;

              private DateTime releasedate;
              public static int count;
              public void Run(string RoadName)
              {
                     Console.WriteLine("我正在" + RoadName + "公路上行驶……");

              }

       }
       class Class_03_03
       {
              public static void Main(String[] args)

              {

                     Car car1 = new Car();

                     Car car2 = new Car();

                     car1.Run("33号");

                     car2.Run("京珠高速");

              }

       }

}

现在两个对象的Run方法输出不一样了,因为传递给它们的参数不相同。car1.Run(“33号”);car2.Run(“京珠高速”);程序中的Run方法接受一个字符串参数,即公路名称,并且在输出的时候反映这一点。如果说参数是方法的“输入”,那么返回值就是方法的“输出”。也就是方法在做完了一件事以后,对外有一个交待。看看下面这个程序有什么反映:

using System;
namespace _03_04
{
       class Car
       {

              public string color;

              private float weight;

              private DateTime releasedate;
              public static int count;
              public float GetWeight()

              {
                     Random rd = new Random();//创建Random类的对象rd

                     weight = (float)rd.NextDouble();//返回一个介于0.0和1.0之间的随机数
                     return weight;

              }

       }
       class Class_03_04
       {
              public static void Main(String[] args)
              {
                    Car car1 = new Car();

                     Car car2 = new Car();

                     Console.WriteLine("car1的重量是" + car1.GetWeight() + "吨");

                     Console.WriteLine("car2的重量是" + car2.GetWeight() + "吨");

              }

       }

}

Car类拥有了一个GetWeight()方法,它随机产生了一个weight值,并且把它返回,而程序就输出了这个返回值。当然,有静态字段就有静态方法、

  • 静态方法也要用static修饰符来说明
  • 在调用静态方法,也需要用类名,而不是实例名
  • 静态方法只能访问类的静态成员,而不能直接访问实例成员

5.类的构造函数

类的构造函数是用来执行类和对象的初始化工作的方法。例如前面的Car对象在产生的时候,color、weight、releasedate都是空值,但是一辆车出厂的时候,这些特征却不应该为空,我们可以用构造函数来初始化这些值。

这段代码中的Car类加入了构造函数,它把新Car对象的color设置为red,weight设置为1.0,releasedate设置为当前的时间:

class Car

{

 string color;

 float weight;

 DataTime releasedate;

public static int count;

public Car()   //构造函数

{ color=”red”;

  weight=1.0f;

  releasedate=DataTime.Today;

}

}

构造函数是不返回值的,但可以编写有参数的构造函数,让它根据参数来初始化类的字段。构造函数不是直接通过方法名来调用,而是在产生一个类实例的时候,通过new来调用。

public Car(string color, float weight) //有参数的构造函数

{color=color;

weight=weight;

releasedate=DataTime.Today;

}

例如现在可以这样产生Car对象了:

Car Car1=new Car();//调用无参数的构造函数

Car Car2=new Car(“blue”,1.5f);//调用有参数的构造函数

现在car1的color为“red”,weight为1.0f,而car2的color为blue,weight为1.5f。

  • 构造函数也有静态的,静态构造函数在类出现的时候会被自动调用
  • 静态构造函数用来初始化静态字段
  • 静态成员可以直接在声明的时候初始化,所有它的典型用途是:当类使用日志文件时,使用这种构造函数向日志文件中写入项。

如:

class Car

{

 static Car()//静态构造函数

{ count=0;}

}

⑴静态构造函数永远时被自动调用的,没有办法直接调用它

⑵静态构造函数即没有访问修饰符,也没有参数

6.方法的重载

刚才的例子中,Car类有两个构造函数,在调用的时候如何区分它们呢?显然,它们的区别在于参数不同:一个没有参数,一个带有两个参数,第一个为string类型,第二个为float类型。在c#中,这种方法参数个数和类型的不同称为方法的“签名”。

不同的方法即使名字相同,但签名不同,也会被视为不同的方法。而编写这些名字相同而签名不同的方法,就叫做方法的重载。上面的car类中,这种方法(public car(string color,float weight))就是对方法public car()无参数的构造函数的重载。当然,我们还能编写其他的不同签名的构造函数。例如,它也有两个参数:public car(float weight,string color),而且只是把前面的一个有参数的构造函数的两个参数调换了一下位置,但是由于同一个位置上参数的类型不同,所以签名不同。(根据参数类型,它不会识别参数的名称)值得注意的是,c#在判断方法的签名时,仅仅只是根据参数类型,它不会识别参数的名称。例如这个方法由于前面的构造函数的参数类型相同,尽管参数名称不同,但却会被c#认为时与前面的构造函数具有相同的签名,因此这种重载方法是不允许的。public car(string color1,float weight1)

不仅仅是构造函数,一般的方法都可以重载。我们来看这个程序:
 using System;
namespace _03_05

{
    class Car
    {
        public string color;

        private float weight;

        private DateTime releasedate;

        public static int count;

        public Car()            // 无参数的构造函数

        {

            color = "Red";

            weight = 1.0F;

            releasedate = DateTime.Today;

        }



        public Car(string Color, float Weight)  // 有参数的构造函数

        {

            color = Color;

            weight = Weight;

            releasedate = DateTime.Today;

        }



        public void Run()

        {

            Console.WriteLine("我正在路上行驶……");

        }



        public void Run(string RoadName)

        {

            Console.WriteLine("我正在" + RoadName + "公路上行驶……");

        }



        public void Run(int Mileage)

        {

            Console.WriteLine("我已经行驶了" + Mileage + "公里……");

        }

    }

   

    class Class_03_05

    {

        public static void Main(String[] args)

        {

            Car car1 = new Car();

            Car car2 = new Car("Blue", 1.5F);



            Console.WriteLine("car1的颜色是" + car1.color);

            Console.WriteLine("car2的颜色是" + car2.color);



            car1.Run();

            car1.Run("51号");

            car1.Run(100);

        }

    }

}

在程序中,car类的构造函数有两个重载版本,而run方法有三个重载版本。在调用的时候,只需要写上方法的名字和参数,而调用哪一个重载最合适的问题,就交给编译器来决定了。

7.类的属性

我们要介绍的第三种类的成员称为属性,属性是类对外表现的自我特征,例如车的颜色和重量,属性就像字段一样可以进行读写,下面的car类就提供了color和weight两个属性。在属性的声明中,可以用get来定义属性的读操作,而set来定义属性的写操作。

using System;



namespace _03_06

{

    class Car

    {

        public string color;

        private float weight;

        private DateTime releasedate;



        public static int count = 0;

       

        public string Color

        {

            get

            {

                return color;

            }

            set

            {

                color = value;

            }

        }



        public float Weight

        {

            get

            {

                return weight;

            }

            set

            {

                weight = value;

            }

        }

    }

   

    class Class_03_06

    {

        public static void Main(String[] args)

        {

                     Car car1 = new Car();



            car1.Color = "Black";

            car1.Weight = 1.0F;

            Console.WriteLine("car1的颜色是" + car1.Color);

            Console.WriteLine("car1的重量是" + car1.Weight);

        }

    }

}

属性的set操作定义中用到了value关键字,它表示对属性进行写操作时提供的参数,例如在car1.color=“black”的时候,value就是“black”,而在car1.weight=1.0f时,value就时1.0f。显然,这里会产生一个问题:属性和字段有什么区别?既然把一个字段声明为public就可以实现对它的读写,那么为什么又要为了提供一个属性而大动干戈?属性比直接的字段读写提供了更多的控制。如果把一个字段声明为public,那么就等于完全放开了对它的控制,任何人都可以对它进行读写。而属性则不同,对于一个属性,我们可以提供get方法以支持读,提供set方法支持写,但是也可以不提供。如果只提供了set方法,这个属性就时只写的。例如对于releasedate这个字段,由于一辆车的出厂日期时在它出厂的时候就确定了,不可以随意改变,所以应该把字段设为private,而提供一个只读的releasedata属性。这时,任何对于属性releasedate进行写操作的企图都会被编译器禁止掉。

Class Car

{

Public DateTime ReleaseDate

{

 Get

 {

   Return releasedate;

}

}

}

其次,属性拥有一些方法的特征,这让它可以达到直接读写字段无法达到的效果。如果我们希望为car类提供一个年龄(age)属性,以告诉用户这辆车已经使用了多久了,该怎么办?但是,随着时间的流逝,这个值会不停的变化,我们要不停的更新它。怎么更新呢?其实汽车的出厂日期已经保存在car类当中了,只需要当前的时间减去出厂日期就可得到。于是,可以为car类型提供age属性,而不是字段。

Public TimeSpan Age

{

Get

{ return(DataTime.Today-ReleaseDate);

}

}

所以在属性age的读操作中包含了一个计算年龄的动作。并且汽车的年龄是不能随意改动的,所以这也是一个只读属性。

下面IT技术网站志在指尖给大家推荐一些有关C#其他的教程,点击下方链接观看吧:

C#基础入门第四篇(如何制作时钟)

C#基础入门第三篇-如何利用C#制作出简单的路程计算程序

C#基础入门第二篇-如何制作简单的计算器 C#基础入门教学-如何利用C#制作简单的计算器

C#基础入门篇一 C#第一个面向桌面的C语言程序(指尖网络)

未经允许不得转载:IT技术网站 » C#基于对象的程序设计(一)
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!

 

志在指尖 用双手敲打未来

登录/注册IT技术大全

热门IT技术

C#基础入门   SQL server数据库   系统SEO学习教程   WordPress小技巧   WordPress插件   脚本与源码下载