● 方法又称成员函数(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)。
● 方法分为静态方法和实例方法。静态方法只可以操作静态域,而实例方法既可以操作静态域,也可以操作实例域。
● 方法有如域一样的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)。