您的位置:首页 > 编程语言 > C#

C#OOP之六 构造函数和析构函数

2016-04-27 20:48 316 查看


方法重载

在C#中,语法规定同一个类中两个或两个以上的方法可以用同一个名字,如果出现此情况,那么该方法 就被称为重载方法。当一个 重载方法被调用时,C#会根据调用该方法的参数自动调用具体的 方法来执行。

每个类型成员都有一个唯一的签名。方法签名由方法名称和一个参数列表(方法的参数的顺序和类型)组成。只要签名不同,就可以在一种类型内定义具有相同名称的多种方法。当定义两种或多种具有相同名称的方法时,就称作重载。参数列表可以通过 varargs
约束来限定,此约束指出方法支持一个变量参数列表。例如,在System.Char 中,IsDigit 被重载。一种方法使用了一个 Char 并返回一个 Boolean。另一种方法使用了一个 String 和一个 Int32,并返回一个 Boolean。

定义重载方法的 规则:

1. 必须是同一个类中的 方法。

2. 满足方法名字相同,参数不同(指参数个数不同或参数个数相同而参数类型不同)。

3. 与方法的访问修饰符和返回值无关。

示例:

n using System;

n namespace TestAdd

n {

n public class TestAdd

n {

n public int Add(int a,int b)

n {

n return a+b;

n }

n public double Add(doublea,double b)

n {

n return a+b;

n }

n publicdecimal Add(decimal a,decimal b)

n {

n return a+b;

n }

n public int Add(string a,stringb)

n {

n returnConvert.ToInt32(a)+Convert.ToInt32(b);

n }

n }

n class Test

n {

n public static void Main()

n {

n TestAdd t=new TestAdd();

n t.Add(1,2);

n t.Add(2.0,3.0);

n t.Add("3","4");

n t.Add(2,3.0);

n }

n }

n }

[b]构造函数[/b]

构造方法和析构方法是系统自动调用的两个方法。前者用于初始化类中的数据成员,后者用于释放类中数据成员所占的资源。

构造函数定义的规则:

构造方法名必须与类同名。

构造方法没有 返回值,除此之外和普通方法定义方式一样。

如果提供了自定义构造方法,则系统将不再提供默认的构造方法。

使用static关键字可以将构造函数声明为静态构造函数。在访问任何静态字段之前,都将自动调用静态构造函数,它们通常用于初始化静态类成员。

静态构造函数具有以下特点:

· 静态构造函数既没有访问修饰符,也没有参数。

· 在创建第一个实例或引用任何静态成员之前,将自动调用静态构造函数来初始化类。

· 无法直接调用静态构造函数。

· 在程序中,用户无法控制何时执行静态构造函数。

· 静态构造函数的典型用途是:当类使用日志文件时,将使用这种构造函数向日志文件中写入项。

· 静态构造函数在为非托管代码创建包装类时也很有用,此时该构造函数可以调用LoadLibrary
方法。

示例:

public class Bus

{

// Static constructor:

static Bus()

{

System.Console.WriteLine("Thestatic constructor invoked.");

}

public static void Drive()

{

System.Console.WriteLine("TheDrive method invoked.");

}

}

class TestBus

{

static void Main()

{

Bus.Drive();

}

}

系统默认的 构造方法是无参数的构造方法,但一旦在类中定义了,无参数的 构造函数或其他构造函数,系统将不再默认该无参构造方法。

[b]析构函数[/b]

析构方法的作用刚好和构造方法相反,构造方法在创建对象时使用,而析构方法是在销毁对象时使用的。销毁对象主要是指系统从内存中删除对象并回收对象所占资源的过程。对象所占资源是指对象正在使用或曾经使用的资源,包括变量所占的内存,应用程序窗口,网络连接,文件句柄,数据库连接等。

当对象不再被应用程序使用时,垃圾回收器会自动调用析构方法,进行资源的释放,析构方法的定义规则和构造方法几乎相同,只是在构造方法前面加“~”符号。

析构方法使用时要注意:

一个类只能有一个析构方法。

无法继承或重载析构方法。

无法自己调用析构方法,他们是被自动调用的。

析构方法不用任何访问修饰符,也没有参数。

示例:

class First

{

~First()

{

System.Console.WriteLine("First'sdestructor is called");

}

}

class Second: First

{

~Second()

{

System.Console.WriteLine("Second'sdestructor is called");

}

}

class Third: Second

{

~Third()

{

System.Console.WriteLine("Third's destructor is called");

}

}

class TestDestructors

{

static void Main()

{

Third t = new Third();

}

}

通过构造函数与析构函数我们可以了解对象的生命周期:从创建对象到对象死亡。

项目1:设计时间类

项目背景:设计一个时间类,以各种格式显示时间。

解决方案:using System;

classTime2

{

private int hour;

private int minute;

private int second;

public Time2(): this(0,0,0){}

public Time2(int h): this(h,0,0){}

public Time2(int h,int m): this(h,m,0){}

public Time2(int h,int m,int s)

{

SetTime(h,m,s);

}

public Time2(Time2 time):this(time.Hour,time.Minute,time.Second){}

public void SetTime(int h,int m, int s)

{

Hour=h;

Minute=m;

Second=s;

}

public int Hour

{

get

{

return hour;

}

private set

{

hour=((value>=0&&value<24)?value : 0);

}

}

public int Minute

{

get

{

return minute;

}

private set

{

minute=((value>=0&&value<60)? value : 0);

}

}

public int Second

{

get

{

return second;

}

private set

{

second=((value>=0&&value<60)? value : 0);

}

}

public string ToUniversalString()

{

returnstring.Format("{0:D2}:{1:D2}:{2:D2}",Hour,Minute,Second);

}

public override string ToString()

{

returnstring.Format("{0:D2}:{1:D2}:{2:D2}{3}",((Hour==0||Hour==12)?12:Hour%12),Minute,Second,(Hour<12 ?"AM":"PM"));

}

}

publicclass Time2Test

{

public static void Main()

{

Time2 t1=new Time2();

Time2 t2=new Time2(2);

Time2 t3=new Time2(21,34);

Time2 t4=new Time2(12,25,42);

Time2 t5=new Time2(27,74,99);

Time2 t6=new Time2(t4);

Console.WriteLine("Constructedwith :\n");

Console.WriteLine("t1: allarguments defaulted");

Console.WriteLine("{0}",t1.ToUniversalString());

Console.WriteLine(" {0}\n",t1.ToString());

Console.WriteLine("t2: hourspecified; minute and second defaulted ");

Console.WriteLine("{0}",t2.ToUniversalString());

Console.WriteLine(" {0}\n",t2.ToString());

Console.WriteLine("t3:hour and minute specified; seconddefaulted ");

Console.WriteLine("{0}",t3.ToUniversalString());

Console.WriteLine(" {0}\n",t3.ToString());

Console.WriteLine("t4:hour , minute and second specified; ");

Console.WriteLine("{0}",t4.ToUniversalString());

Console.WriteLine(" {0}\n",t4.ToString());

Console.WriteLine("t5:all invalid values specified ");

Console.WriteLine("{0}",t5.ToUniversalString());

Console.WriteLine(" {0}\n",t5.ToString());

Console.WriteLine("t6:Time2object t4 specified ");

Console.WriteLine("{0}",t6.ToUniversalString());

Console.WriteLine(" {0}\n",t6.ToString());

}

}

属性

属性结合了字段和方法的多个方面。对于对象的用户,属性显示为字段,访问该属性需要完全相同的语法。对于类的实现者,属性是一个或两个代码块,表示一个get访问器和/或一个set访问器。当读取属性时,执行
get 访问器的代码块;当向属性分配一个新值时,执行 set
访问器的代码块。不具有 set
访问器的属性被视为只读属性。不具有 get
访问器的属性被视为只写属性。同时具有这两个访问器的属性是读写属性。


public class Date

{

private int month = 7; //"backing store"

public int Month

{


get



{



return month;



}



set


{


if ((value > 0) &&(value < 13))



{



month = value;



}



}


}

}

在本例中,Month是作为属性声明的,这样 set
访问器可确保Month值设置为 1
和 12 之间。Month属性使用私有字段来跟踪实际值。属性的数据的真实位置经常称为属性的“后备存储”。属性使用作为后备存储的私有字段是很常见的。将字段标记为私有可确保该字段只能通过调用属性来更改。

Get访问器

get 访问器体与方法体相似。它必须返回属性类型的值。执行 get
访问器相当于读取字段的值。例如,当正在从 get 访问器返回私有变量并且启用了优化时,对 get 访问器方法的调用由编译器进行内联,因此不存在方法调用的系统开销。然而,由于在编译时编译器不知道在运行时实际调用哪个方法,无法内联虚拟 get 访问器。以下是返回私有字段
name的值的 get 访问器:

class Person

{

private string name; // the namefield

public string Name // the Nameproperty

{

get

{

return name;

}

}

}

set 访问器类似于返回类型为void的方法。它使用称为 value
的隐式参数,此参数的类型是属性的类型。在下面的示例中,将 set
访问器添加到
Name
属性:

示例1:

class Person

{

private string name; // the namefield

public string Name // the Nameproperty

{

get

{

return name;

}

set

{

name = value;

}

}

}

当对属性赋值时,用提供新值的参数调用 set访问器。

public class Employee

{

public static int NumberOfEmployees;

private static int counter;

private string name;

public string Name

{

get { return name; }

set { name = value; }

}

public static int Counter

{

get { return counter; }

}

public Employee()

{

counter = ++counter +NumberOfEmployees;

}

}

class TestEmployee

{

static void Main()

{

Employee.NumberOfEmployees = 100;

Employee e1 = new Employee();

e1.Name = "Claude Vige";

System.Console.WriteLine("Employeenumber: {0}", Employee.Counter);

System.Console.WriteLine("Employeename: {0}", e1.Name);

}

}



实践问题:

选择题:

1. 下面哪个关于垃圾收集的语句是正确的?

a. 在垃圾收集中, 对象没有被销毁.

b. 在垃圾收集中,对象每次都被销毁.

c. 在垃圾收集中,仅非引用对象被销毁.

2. 一个析构函数有与它的类一样的名字,但是有一个前缀______________.

3. 在C#中,___________过程识别不使用的对象并且释放他们.

4. 考虑下面代码,确定哪行将在编译的时候产生错误:

using System;

namespace Object6s

{

class Draw

{

public void Shape()

{

Console.WriteLine(“in shape method”);

}

public Draw()//line1

{

Console.WriteLine(“This is aconstructor”);

}

public static void main()

{

Draw obj =new Draw();

obj.Draw();//line3

obj.Shape();//line4

}

}

}

考虑上述代码,并回答下面的问题.在上面代码的哪行将在编译时产生错误?

A . line1

B. line2

C. line3

D. line4

5.IDisposable 接口包含_____________方法.

小结:

在本章中,我们主要学习了:

u 构造函数是类的成员函数,当它所属的类被创建的时候被调用。

u 构造函数有与它的类相同的名字。

u 析构函数在类实例停止存在的时候被调用。

u 析构函数有与类一样的名字,但是它有一个前缀~(鼻音化符号)。

u 构造函数是一个特殊方法,它允许控制对象的初始化。

u 析构函数是用于从内存中释放类实例的特殊方法。

u 垃圾收集是自动释放不使用的对象内存的过程

u Finalize()析构函数在对象的最后引用被从内存释放之后,被调用。

u Dispose()方法被调用来释放一个资源,例如数据库连接,但使用这样的资源的对象不在被使用的时候。

u 术语多态来自与希腊词语‘poly’和 ‘morphos’,它们相应的表示‘许多’和‘形式’。

u 多态允许一个接口被多个函数使用。

u 静态多态指一个实体,它同时以不同形式存在。

u 在动态多态中,关于函数执行的决定是在代码执行的时候确定。

u 函数重载是为类中的两个或多个函数使用同一名字的过程。

u 数量,类型或函数的参数顺序被称为函数签名。

u 重载构造函数通常被在C#中创建对象的时候提供灵活性。

u 操作符重载提供额外的应用与用户定义的数据类型时的C#操作符的能力。

u 预定义C#操作符可以使用operator关键字来重载。

u 操作符可以被认为是编译器内部的函数。

英语词汇:

英文名 全文 翻译

Constructor 构造

Destructor 析构

Invoke 被调用

Finalize 最终化

Dispose 处理、处置

CLR CommonLanguage Runtime 公共语言运行环境

练习项目:

创建Invoice类,商店用其打印所出售项目的发票。Invoice要包括四个实例变量

————零件号(string类型),零件名(string类型),数量(int类型)和单价(decim类型)。类的构造函数要初始化这些实例变量。对每个实例变量提供一个属性,包括get和set方法。此外,提供GetInvoiceAmount方法,计算发票金额(即数量乘以单价),然后返回decimal值。如果数量为负值,则保持不变。同样,如果单价为负值,则保持不变。写一个测试程序InvoiceIext,演示Invoice的功能。

创建Employee类,包括三个实例变量:名字(string类型)、姓氏(string类型)、和月薪(decimal)。类的构造函数要初始化这些实例变量。对每个实例变量提供一个属性,包括get和set方法。如果月薪为负值,则保持不变。写一个测试程序EmployeeTest,演示Employee的功能。创建两个Employee对象,显示每个对象的年薪,然后让每个员工提薪10%,再次显示每个对象的年薪。

创建Date类,包括三个实例变量:月(int类型)、日(int类型)、和年(int类型)。类的构造函数要初始化三个实例变量,保证提供的值正确。对每个实例变量提供一个属性,包括get和set方法。提供DisplayDate方法,显示月、日、年并用斜杠(/)分隔。写一个测试程序DateTest,演示Date的功能。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: