C#语言系列讲座(6) 方法

来源:互联网 发布:编辑文档的软件 编辑:程序博客网 时间:2024/05/21 00:48
● 方法又称成员函数(Member Function),集中体现了类或对象的行为。

● 方法分为静态方法和实例方法。静态方法只可以操作静态域,而实例方法既可以操作静态域,也可以操作实例域。

● 方法有如域一样的5种存取修饰符——public、protected、internal、protected internal、private。

方法参数

方法又称成员函数(Member Function)。

方法的参数传递有四种类型:传值(By value),传址(By reference),输出参数(By output),数组参数(By array)。传值参数无需额外的修饰符,传址参数需要修饰符ref,输出参数需要修饰符out,数组参数需要修饰符params。传值参数在方法调用过程中如果改变了参数的值,传入方法的参数在方法调用完成以后并不因此而改变,而是保留原来传入时的值。传址参数恰恰相反,如果方法调用过程改变了参数的值,那么传入方法的参数在调用完成以后也随之改变。实际上从名称上可以清楚地看出两者的含义——传值参数传递的是调用参数的一份拷贝,而传址参数传递的是调用参数的内存地址,该参数在方法内外指向的是同一个存储位置。 注意传址参数的方法调用无论在声明时还是调用时都要加上ref修饰符。

在传值和传址情况下,C#强制要求参数在传入之前由用户明确初始化,否则编译器报错!但如果有一个并不依赖于参数初值的函数,只是需要函数返回时得到它的值时该怎么办呢(往往在函数返回值不止一个时特别需要这种技巧)?答案是用由out修饰的输出参数。但需要注意输出参数与通常的函数返回值有一定的区别:函数返回值往往存在堆栈里,在返回时弹出;而输出参数需要用户预先指定存储位置,也就是用户需要提前声明变量——当然也可以初始化。

在函数体内所有输出参数必须被赋值,否则编译器报错!out修饰符同样应该用在函数声明和调用中。除了充当返回值这一特殊的功能外,out修饰符与ref修饰符有很相似的地方:传址。可以看出C#完全摈弃了传统C/C++语言赋予程序员的很大的自由度,毕竟C#是用来开发的下一代高效的网络平台,安全性——包括系统安全(系统结构的设计)和工程安全(避免程序员经常犯的错误)是它设计时的重要考虑,当然C#并没有因为安全性而丧失多少语言的性能,这正是C#的卓越之处!

数组参数用来传递大量的数组集合参数。数组参数可以是数组(如:var),也可以是能够隐式转化为数组的参数,如: 10,20,30,40,50。这为程序提供了很高的扩展性。

同名方法参数的不同会导致方法出现多态现象,这又叫重载(overloading)方法。需要指出的是编译器是在编译时便绑定了方法和方法调用。只能通过参数的不同来重载方法,其他的不同(如返回值)不能为编译器提供有效的重载信息。

方法继承

C#的方法引入了virtual、override、sealed、abstract四种修饰符来提供不同的继承需求。类的虚方法是可以在该类的继承类中改变其实现的方法,当然这种改变仅限于方法体的改变,而非方法头(方法声明)的改变。被子类改变的虚方法必须在方法头加上override来表示。当一个虚方法被调用时,该类的实例——亦即对象的运行时类型(run-time type)来决定哪个方法体被调用。看下面的例子:

using System;

class Parent

{ public void F() {

Console.WriteLine(“Parent.F”); }

public virtual void G() {

Console.WriteLine(“Parent.G”); }

}

class Child: Parent

{ new public void F() {

Console.WriteLine(“Child.F”); }

public override void G() {

Console.WriteLine(“Child.G”); }

}

class Test

{

static void Main()

{ Child b = new Child();

Parent a = b;

a.F();

b.F();

a.G();

b.G();

}

}

程序经编译后执行,输出:

Parent.F

Child.F

Child.G

Child.G

可以看到类Child中F()方法的声明采取了重写(new)来屏蔽父类中的非虚方法F()的声明,而G()方法采用了覆盖(override)来提供方法的多态机制。需要注意的是重写(new)方法和覆盖(override)的不同,从本质上讲重写方法是编译时绑定,而覆盖方法是运行时绑定。值得指出的是虚方法不可以是静态方法——也就是说不可以用static和virtual同时修饰一个方法,这是由它的运行时类型辨析机制所决定的。override必须和virtual配合使用,当然也就不能和static同时使用。

如果在一个类的继承体系中不想再使一个虚方法被覆盖,该怎样做呢?答案是sealed override(密封覆盖),用sealed和override同时修饰一个虚方法便可以达到这种目的: sealed override public void F()。注意这里一定是sealed和override同时使用,也一定是密封覆盖一个虚方法,或者一个被覆盖(而不是密封覆盖)了的虚方法。密封一个非虚方法是没有意义的,也是错误的。

抽象(abstract)方法在逻辑上类似于虚方法,只是不能像虚方法那样被调用,是一个接口的声明而非实现。抽象方法没有方法实现,也不允许这样做。抽象方法同样不能是静态的。含有抽象方法的类一定是抽象类,也一定要加abstract类修饰符。但抽象类并不一定要含有抽象方法。继承含有抽象方法的抽象类的子类必须覆盖并实现(直接使用override)该方法,或者组合使用abstract override使之继续抽象,或者不提供任何覆盖和实现,后两者的行为是一样的。看下面的例子:

using System;

abstract class Parent

{ public abstract void F();

public abstract void G(); }

abstract class Child: Parent

{ public abstract override void F();}

abstract class Grandson: Child

{

public override void F()

{ Console.WriteLine(“Grandson.F”);}

public override void G()

{ Console.WriteLine(“Grandson.G”);}

}

抽象方法可以抽象一个继承来的虚方法。抓住了运行时绑定和编译时绑定的基本机理,便能看透方法呈现出的overload、virtual、override、sealed、abstract等形态。

外部方法

C#引入了extern修饰符来表示外部方法。外部方法是用C#以外的语言实现的方法如Win32 API函数。外部方法不能是抽象方法。看下面的一个例子:

using System;

using System.Runtime.InteropServices;

class MyClass

{ [DllImport(“user32.dll”)]

static extern int MessageBoxA(int hWnd, string msg,string caption, int type);

public static void Main()

{MessageBoxA(0,“Hello, World! ”, “This is called from a C# app! ”, 0); }

}

程序经编译后执行,输出:



这里调用了Win32 API函数int MessageBoxA(int hWnd, string msg,string caption, int type)。
原创粉丝点击