类中的方法就像语句中的动词一样。方法要完成某些动作,正是这些动作定义了类的行为。方法由其签名来标识,签名由方法名,参数个数,以及每个参数的数据类型组成。只要没有其他同名而且参数表匹配的方法,方法签名就是唯一的。除了参数外方法还有一个返回类型(不返回任何结果为void),以及一个修饰符列表,用来确定方法的可访问性和多态行为。
多态(polymorphic):多态是指类有相同的方法,但这些方法的实现不同。想一想,比如所有类型都实现了ToString方法。当在int类型上使用ToString方法时,它将数值显示为文本;而在类实例上使用时,则显示底层类的类名。
使用.NET的难点之一就是要理解方法修饰符在定义应用多态行为时所扮演的角色。基类使用方法修饰符来指示某个方法可以被覆盖,或子类必须实现该方法;
方法修饰符
除了方法修饰符外,方法还可以使用另外7种修饰符,其中的 new、virtual、override、sealed和abstract等5种修饰符支持多态。
修饰符
|
说明
|
static
|
该方法是类的一部分,而不是类实例的一部分。这就意味着可以指定classname.method(parameters)来直接访问类,而无需创建类实例。
|
virtual
|
指示该方法可以在子类中覆盖,它不能与static或private访问修饰符同一使用。
|
override
|
指示该方法覆盖了基类中的同名方法,这样它就能定义子类特有的行为。基类中被覆盖的方法必须是virtual方法(非虚法)
|
new
|
允许继承类中的一个方法“隐藏”基类中同名的非虚方法。它会取代原方法,而不是覆盖。
|
sealed
|
禁止派生类覆盖此方法。
l
用在派生类中,该类又会作为基类派生自己的子类。
l
必须与override修饰符一起使用。
|
abstract
|
该方法不包含具体实现细节,而且必须有子类实现。只能用作abstract类的成员。
|
extern
|
指示该方法是在外部实现的。它常与DLLImport属性一起使用。DLLImport属性指示要由一个DLL提供实现细节。
|
-
静态修饰符
与其他类成员一样,static修饰符定义一个成员的行为是针对类的全局行为,而不是特定于某个类实例。有些辅助类无需实例化就可以使用,static修饰符就大多用在这些辅助类的构造函数和方法中。
例子:
class
Conversions
{
private static double cmperInch = 2.54 ;
private static double gmPerPound = 455 ;
public static double inchesToMetric( double inches){ return (inches * cmperInch); }
public static double poundsToGrams( double pounds){ return (pounds * gmPerPound); }
}
class Test
{
static void Main()
{
double cm, grams;
cm = Conversions.inchesToMetric( 28.5 );
grams = Conversions.poundsToGrams( 984.4 );
}
}
{
private static double cmperInch = 2.54 ;
private static double gmPerPound = 455 ;
public static double inchesToMetric( double inches){ return (inches * cmperInch); }
public static double poundsToGrams( double pounds){ return (pounds * gmPerPound); }
}
class Test
{
static void Main()
{
double cm, grams;
cm = Conversions.inchesToMetric( 28.5 );
grams = Conversions.poundsToGrams( 984.4 );
}
}
-
使用virtual和override修饰符的方法继承
通过继承,程序创建新的类可以遵循已有基类形式,并沿袭基类的功能。之后新类可以增加代码,从而与基类在行为上有所区别。子类和基类对同一个消息可能有不同的响应,这就是典型的多态。在实际应用中,多态更多的是指基类和派生类为具有相同签名的方法提供了不同的实现代码。
默认情况下不能在派生类中修改基类中的方法。为了摆脱这个限制,.NET提供了virtual修饰符作为提示,它会告诉编译器基类中的某个方法可以在子类(继承了这个方法)中重新定义。同样地,编译器要求任何改变基类中虚方法派生类都应当在该方法前使用oerride修饰符。
虚方法
class
Fiber
{ public virtual string ShowMe() { return ( " Base " ); }}
class Natural : Fiber
{ public override string ShowMe() { return ( " Natural " ); }}
class Cotton : Natural
{ public override string ShowMe() { return ( " Cotton " ); }}
class Test
{
static void Main()
{
Fiber fib1 = new Natural();
Fiber fib2 = new Cotton();
string fibVal;
fibVal = fib1.ShowMe(); // returns “Natural”
fibVal = fib2.ShowMe(); // returns “cotton”
}
}
{ public virtual string ShowMe() { return ( " Base " ); }}
class Natural : Fiber
{ public override string ShowMe() { return ( " Natural " ); }}
class Cotton : Natural
{ public override string ShowMe() { return ( " Cotton " ); }}
class Test
{
static void Main()
{
Fiber fib1 = new Natural();
Fiber fib2 = new Cotton();
string fibVal;
fibVal = fib1.ShowMe(); // returns “Natural”
fibVal = fib2.ShowMe(); // returns “cotton”
}
}
本例中,每个子类都为虚方法ShowMe提供了自己的覆盖代码。
-
new 修饰符与版本
在某些情况下,可能有必要隐藏从基类继承的某些成员。例如,类的方法可能与基类中的某个方法有相同的签名,为了告诉编译器子类有自己的方法版本时,就应该在声明中使用new修饰符。
class
Fiber
{
public string ShowMe() { return ( " Base " ); }
public virtual string GetID() { return ( " BaseID " ); }
}
class Natural : Fiber
{
new public string ShowMe() { return ( " Natural " ); }
public override string GetID() { return ( " NaturalID " ); }
}
{
public string ShowMe() { return ( " Base " ); }
public virtual string GetID() { return ( " BaseID " ); }
}
class Natural : Fiber
{
new public string ShowMe() { return ( " Natural " ); }
public override string GetID() { return ( " NaturalID " ); }
}
-
sealed 和abstract修饰符
saaled
修饰符指示不能在继承子类中覆盖该方法。abstract修饰符则要求继承子类必须实现该方法,这种情况中基类提供了方法声明,但没有提供实现。
注意:sealed修饰符总是和override修饰符成对使用。
class
A
{
public virtual void PrintID {... }
}
class B : A
{
sealed override public void PrintID {... }
}
class C : B
{
override public void PrintID {... }
}
{
public virtual void PrintID {... }
}
class B : A
{
sealed override public void PrintID {... }
}
class C : B
{
override public void PrintID {... }
}
抽象(abstract)方法表示一个函数有签名,但没有实现代码,继承了这个抽象方法的所有非抽象必须具体定义该方法。
抽象方法只能在抽象类中声明,不过抽象类可以有非抽象方法。
方法体只包含一个分号(;):public abstract void myMethod();
虽然adstract隐含有virtual的意思,但抽象的方法不能使用virtual修饰符。
虚方法可以被抽象方法覆盖。
传递参数
C#
提供了两个修饰符来指示参数要按引用传递(传引用):out和ref。这两个关键字都能做到将参数的地址传递给目标方法。但究竟使用那个关键字,取决于参数是由调用方法初始化,还是由被调用方法初始化。若是由调用方法初始化参数值,则使用ref关键字,否则,当被调用方法初始化参数值时,要使用out关键字。通过这些关键字,C#要求程序员明确指定被调用方法是要修改参数值,还是要初始化参数值,从而提高代码的可读性。
使用参数修饰符ref和out
class
TestParms
{
public static void FillArray( out double [] prices)
{
prices = new double [ 4 ] { 50.00 , 80.00 , 120.00 , 200.00 };
}
public static void UpdateArray( ref double [] prices)
{
prices[ 0 ] = prices[ 0 ] * 1.50 ;
prices[ 1 ] = prices[ 1 ] * 2.0 ;
}
public static double Taxval( double ourPrice, out double taxAmt)
{
double totval = 1.10 * ourPrice;
taxAmt = totval - ourPrice;
ourPrice = 0.0 ;
return totval;
}
}
class Myapp
{
public static void Main()
{
double [] priceArray;
double taxAmt;
TestParms.FillArray( out priceArray);
Console.WriteLine(priceArray[ 1 ].ToString());
TestParms.UpdateArray( ref priceArray);
Console.WriteLine(priceArray[ 1 ].ToString());
double ourprice = 150.00 ;
double newtax = TestParms.Taxval(ourprice, out taxAmt);
Console.WriteLine(taxAmt.ToString());
Console.WriteLine(ourprice);
}
}
{
public static void FillArray( out double [] prices)
{
prices = new double [ 4 ] { 50.00 , 80.00 , 120.00 , 200.00 };
}
public static void UpdateArray( ref double [] prices)
{
prices[ 0 ] = prices[ 0 ] * 1.50 ;
prices[ 1 ] = prices[ 1 ] * 2.0 ;
}
public static double Taxval( double ourPrice, out double taxAmt)
{
double totval = 1.10 * ourPrice;
taxAmt = totval - ourPrice;
ourPrice = 0.0 ;
return totval;
}
}
class Myapp
{
public static void Main()
{
double [] priceArray;
double taxAmt;
TestParms.FillArray( out priceArray);
Console.WriteLine(priceArray[ 1 ].ToString());
TestParms.UpdateArray( ref priceArray);
Console.WriteLine(priceArray[ 1 ].ToString());
double ourprice = 150.00 ;
double newtax = TestParms.Taxval(ourprice, out taxAmt);
Console.WriteLine(taxAmt.ToString());
Console.WriteLine(ourprice);
}
}