分享

面向对象的编程基础

 Frank_Chia 2009-08-08
                         
                            
面向对象的编程基础
 

       目前流行的各类应用软件,从本质上来说,都是计算机对现实世界的模拟。现实世界中的实体对象,在软件系统中,均表现为需要处理的数据。在面向对象的系统中,这是通过类和对象来表示的。

3.1  面向对象的程序设计

    在面向对象概念中,类是封装数据的基本单位。类是一组具有相同数据结构和相同操作的对象的集合,用来定义对象可执行的操作,比如方法、事件、属性等。类的实例是对象,可以通过调用对象的属性、方法、事件,来访问对象的功能。

    类和对象的区别可以用现实生活中的例子来说明。如果把汽车看成是一个类的话,则生活中的每一辆汽车都是一个对象,也可以说是类的一个实例。

3.1.1  类的组织

    与类有关的一些基本概念有:类的声明、对象的生存周期、字段和局部变量、静态成员与实例成员以及访问修饰符等。

1. 类的声明

    C#中使用class定义类,声明类的形式为:

[附加声明] [访问修饰符] class 类名称[[基类] [,接口序列]]

{

    [字段声明]

    [构造函数]

    [方法]

    [事件]

}

    其中,[ ]中的内容为可选项,冒号(:)后面表示被继承的类或者接口。

    当一个类从另一个类继承时,被继承的类叫做基类。注意,在C#中,基类只能有一个。但是一个类可以继承自多个接口,当接口序列多于一项时,各项之间用逗号分开。如果既有基类又有接口,则要把基类放在冒号后面的第一项,然后才是接口名。

    3-1说明如何声明类、字段、构造函数和方法,同时说明如何实例化对象以及如何输出实例数据。例中声明了两个类,一个是Child类,另一个是包含Main方法的Program类。

【例3-1】声明类、字段、构造函数和方法。

using System;

using System.Collections.Generic;

using System.Text;

namespace StatementExample

{

    public class Child

    {

        private int age;

        private string name;

        // 不带参数的构造函数

        public Child()

        {

            name = "none";

        }

        // 带参数的构造函数

        public Child(string name, int age)

        {

            this.name = name;

            this.age = age;

        }

        // 输出方法

        public void PrintChild()

        {

            Console.WriteLine("{0}, {1} years old.", name, age);

        }

    }

    public class Program

    {

        public static void Main()

        {

            //使用new关键字创建对象,new后是调用的构造函数

            Child child1 = new Child("Zhang San", 11);

            Child child2 = new Child("Li Si", 10);

            Child child3 = new Child();

            // 显示结果

            Console.Write("Child #1: ");

            child1.PrintChild();

            Console.Write("Child #2: ");

            child2.PrintChild();

            Console.Write("Child #3: ");

            child3.PrintChild();

            Console.ReadLine();

        }

    }

}

    输出结果:

Child #1: Zhang San, 11 years old.

Child #2: Li Si, 10 years old.

Child #3: none, 0 years old.

2. 对象的生存周期

    对象的生存周期并不是其所在的区域决定的,它包含以下特性:

    1) 对象在被创建时分配内存,创建对象实际上做了两个方面的工作:使用new保留字要求系统分配内存;使用构造函数初始化数据。

    2) Java相似,C#也是采用垃圾回收机制自动销毁对象,即在适当的时候自动启动回收机制,然后检测没有被引用的对象并将其销毁。实际上,销毁对象也是做了两个方面的工作:释放占用的内存;将分配给对象的内存归还给堆(Heap)。


3. 字段
和局部变量

    为了区分作用域不同的对象或值类型的变量,C#对其进行了更细的划分:把类一级的对象或值类型的变量叫做字段;把在方法、事件以及构造函数内声明的变量叫做局部变量。声明变量的位置不同,其作用域也不同。

【例3-2】定义字段和局部变量。

using System;

using System.Collections.Generic;

using System.Text;

namespace DefinitionExample

{

    public class Program

    {

        public static int j=20;      //定义字段j

        public static void Main()

        {

            int j=30;            //定义局部变量j

            Console.WriteLine(j);      //输出结果:30

            Console.WriteLine(Program.j);  //输出结果:20

            Console.ReadLine();

        }

    }

}

    在这个例子中,定义了一个字段j,是类一级的变量;在Main方法中又定义了一个同名的局部变量j,此时,在Main方法中声明的新变量j隐藏了同名的类一级的变量,所以第一条Console语句输出的结果是30

    当字段和局部变量名相同时,如果要引用静态字段,可以使用下面的形式:

类名.字段名

    如果是实例字段,则使用下面的形式:

this.字段名

    这里的this指当前实例。

    当然,如果没有出现字段和局部变量名重名的情况,引用字段的形式和引用局部变量的形式相同。

4. 静态成员与实例成员

    在类中定义的数据称为类的数据成员,数据成员包含字段、常量和事件等。而函数成员则提供操作类中数据的某些功能,函数成员包括方法、属性、构造函数等。对象中的数据成员和方法一般都是对象私有的,即只有对象本身才能够操作这些数据成员和调用这些方法,其他对象不能直接对其进行操作。可是有一些成员是所有对象共用的,如果把这样的成员保存在每一个对象上,当创建了一个类的多个对象时,在堆中就会出现很多相同的内容,显然浪费资源。此外,若要修改这些成员,也必须对每一个对象都进行修改,很明显这样做效率也比较低。

    解决这个问题的办法是将成员定义为静态(static)的,当该类被装入内存时,系统就会在内存中专门开辟一部分区域保存这些静态成员,这样一来,其他类不必建立该类的实例就可以直接使用该类的静态成员。

    把只有创建了类的实例才能够使用的成员称为实例成员。

    需要注意的是,静态成员在内存中只有一份,不像实例成员可以有多个。而且静态成员要等到应用程序结束时才会消失,所以使用时要根据具体情况决定是否使用静态成员。

    C#中通过指定类名来调用静态成员,通过指定实例名来调用实例成员。

5. 访问修饰符

    将数据和方法封装在类中是为了便于对数据和方法进行控制和修改,访问修饰符用于控制类中数据和方法的访问权限,C#中有以下几种成员访问修饰符。

    1) public

    指任何外部的类都可以不受限制的存取这个类的方法和数据成员。

    2) private

    指类中的所有方法和数据成员只能在此类中使用,外部无法存取。

    3) protected

    除了让本身的类可以使用之外,任何继承自此类的子类都可以存取。

    4) internal

    在当前项目中都可以存取。该访问权限一般用于基于组件的开发,因为它可以使组件以私有方式工作,而该项目外的其他代码无法访问。

    5) protected internal

    只限于当前项目,或者从该项目的类继承的类才可以存取。

    6) partial

    局部类型,类的定义和实现可以分布在多个文件中,但都要使用partial标注。注意,基类只需要声明一次,若多次声明则必须完全一致。

     3.1.2  构造函数

    构造函数是一个特殊的方法,用于在建立对象时进行初始化的动作,在C#中,每当创建一个对象时,都会先调用类中定义的构造函数。

    使用构造函数的好处是它能够确保每一个对象在被使用之前都适当地进行了初始化的动作。

    另外,构造函数还具有以下特点:

    1) 每个类至少有一个构造函数。如果程序代码中没有构造函数,则系统会自动提供一个默认的构造函数。

    2) 一个构造函数总是和它的类名相同。

    3) 构造函数不包含任何返回值。

    4) 构造函数总是public的。

    一般在构造函数中作初始化工作,对于执行过程用时比较长的程序代码,最好不要放在构造函数中。

1. 默认构造函数

    如果在类中不定义构造函数,系统会自动提供一个默认的构造函数,默认构造函数没有参数。提供默认构造函数的目的是为了保证能够在使用对象前先对未初始化的非静态类成员进行初始化工作,即将非静态成员初始化为下面的值:

    1) 对数值型,比如intdouble等,初始化为0

    2) bool类型,初始化为false

    3) 对引用类型,初始化为null

    如果在程序代码中定义了类的构造函数,则所有初始化工作由编程者自己完成。

2. 重载构造函数

    有时候可能会遇到这样的情况:在一个类中的多个方法中都要用到某一个数据成员,而该成员值必须从其他类中传递过来。这时,无参数的构造函数就不能胜任了,解决这个问题最好的办法就是:重载(Overloading)构造函数。

【例3-3】重载构造函数。

using System;

using System.Collections.Generic;

using System.Text;

namespace OverloadingExample

{

    class Program

    {

        public Program()

        {

            Console.WriteLine("null");

        }

       public Program(string str)

       {

           Console.WriteLine(str);

       }

       static void Main()

       {

           Program aa = new Program();

           Program bb = new Program("How are you!");

           Console.ReadLine();

       }

    }

}

    输出结果:

null

How are you!3.1.3  方法

    方法(Method)是一组程序代码的集合,每个方法都有一个方法名,便于识别和让其他方法调用。

1. 方法的定义与使用

    C#程序中定义的方法都必须放在某个类中。定义方法的一般形式为:

访问修饰符 返回值类型 方法名称(参数序列)

{

    语句序列

}

    在定义方法时,需要注意以下几点:

    1) 方法名称后面的小括号中可以有参数序列,也可以没有参数,但是不论是否有参数,小括号都是必需的。如果参数序列中的参数有多个,则以逗号分隔开。

    2) 如果要结束某个方法的执行,可以使用return语句。程序遇到return语句后,会将执行流程交还给调用此方法的程序代码段。此外,还可以利用return语句返回一个值,注意,return语句只能返回一个值。

    3) 如果声明一个void类型的方法,return语句可以省略不写;如果声明一个非void类型的方法,则方法中必须至少有一个return语句。

【例3-4】定义和调用方法。

using System;

using System.Collections.Generic;

using System.Text;

namespace MethodExample

{

    class Program

    {

        public int MyMethod()

        {

            Console.WriteLine("this is MyMethod.");

            int i = 10;

            return i;

        }

        static void Main()

        {

            Program method = new Program();

            int j = 5;

            j = method.MyMethod();

            Console.WriteLine("the value is {0}.", j);

            Console.ReadLine();

        }

    }

}

    输出结果:

this is MyMethod.

the value is 10.

2. 方法中的参数传递

    定义方法时可以将参数传入方法中进行处理,也可以将方法中处理过的信息返回给调用者。传递变量参数到方法的方式有下面几种:

    1) 传递值类型的参数

    值类型参数的格式为:

参数类型 参数名

    定义值类型参数的方式很简单,只要注明参数类型和参数名即可。当该方法被调用时,便会为每个值类型参数分配一个新的内存空间,然后将对应的表达式运算的值复制到该内存空间。在方法中更改参数的值不会影响到这个方法之外的变量。

【例3-5】方法中值类型参数的传递。

using System;

using System.Collections.Generic;

using System.Text;

namespace ValueTransferExample

{

    class Program

    {

        public static void AddOne(int a)

        {

            a++;

        }

        static void Main()

        {

            int a = 3;

            Console.WriteLine("调用AddOne之前,a={0}", a);

            AddOne(a);

            Console.WriteLine("调用AddOne之后,a={0}", a);

            Console.ReadLine();

        }

    }

}

    输出结果:

调用AddOne之前,a=3

调用AddOne之后,a=3

    2) 传递引用类型的参数

    引用类型参数的格式为:

ref 参数类型 参数名

    与传递值类型参数不同,引用类型的参数并没有再分配内存空间,实际上传递的是指向原变量的指针,既引用参数和原变量保存的是同一个地址。为了和传递值类型参数区分,前面加上ref关键字(Reference),在方法中修改引用参数的值实际上也就是修改被引用的变量的值。

【例3-6】方法中引用类型参数的传递。

using System;

using System.Collections.Generic;

using System.Text;

namespace ReferenceTransferExample

{

    class Program

    {

        public static void AddOne(ref int a)

        {

            a++;

        }

        static void Main()

        {

            int x = 3;

            Console.WriteLine("调用AddOne之前,x={0}", x);

            AddOne(ref x);

            Console.WriteLine("调用AddOne之后,x={0}", x);

            Console.ReadLine();

        }

    }

}

    输出结果:

调用AddOne之前,x=3

调用AddOne之后,x=4

    3) 输出多个引用类型的参数

    有时候一个方法计算的结果有多个,而return语句一次只能返回一个结果,这时就用到了out关键字,使用out表明该引用参数是用于输出的,而且调用该参数时不需要对参数进行初始化。

    输出引用类型参数的格式为:

out 参数类型 参数名

【例3-7】输出多个引用类型的参数。

using System;

using System.Collections.Generic;

using System.Text;

namespace ReferenceOutExample

{

    class Program

    {

        public static void MyMethod(out int a, out int b)

        {

            a = 5;

            b = 6;

        }

        static void Main()

        {

            int x, y;

            MyMethod(out x, out y);

            Console.WriteLine("调用MyMethod之后,x={0},y={1}", x, y);

            Console.ReadLine();

        }

    }

}

    输出结果:

调用MyMethod之后,x=5,y=6

    4) 传递个数不确定的参数

    当需要传递的参数个数不确定时,比如求几个数的平均值,由于没有规定是几个数,运行程序时,每次输入的值的个数就不一定一样。为了解决这个问题,C#采用params关键字声明参数的个数是不确定的。

【例3-8】传递个数不确定的参数。

using System;

using System.Collections.Generic;

using System.Text;

namespace UncertaintyTransferExample

{

    class Program

    {

        public static float Average(params long[] v)

        {

            long total, i;

            for (i = 0, total = 0; i < v.Length; ++i)

                total += v[i];

            return (float)total / v.Length;

        }

        static void Main()

        {

            float x = Average(1, 2, 3, 5);

            Console.WriteLine("1235的平均值为{0}", x);

            x = Average(4, 5, 6, 7, 8);

            Console.WriteLine("45678的平均值为{0}", x);

            Console.ReadLine();

        }

    }

}

    输出结果:

1235的平均值为2.75

45678的平均值为6

    方法重载是指具有相同的方法名,但参数类型或参数个数不完全相同的多个方法可以同时出现在一个类中。这种技术非常有用,在开发过程中,我们会发现C#中的很多方法均使用了重载技术。

【例3-9】方法重载。

using System;

using System.Collections.Generic;

using System.Text;

namespace MethodOverloadingExample

{

    class Program

    {

        public static int Add(int i, int j)

        {

            return i + j;

        }

        public static string Add(string s1, string s2)

        {

            return s1 + s2;

        }

        public static long Add(long x)

        {

            return x + 5;

        }

        static void Main()

        {

            Console.WriteLine(Add(1, 2));

            Console.WriteLine(Add("1", "2"));

            Console.WriteLine(Add(10));

            //按回车键结束

            Console.ReadLine();

        }

    }

}

    输出结果:

3

12

15

    在这个例子中,虽然有多个Add方法,但由于方法中参数的个数和类型不完全相同,所以系统在调用时会自动找到最匹配的方法。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多