欢迎来到.net学习网

欢迎联系站长一起更新本网站!QQ:879621940

您当前所在位置:首页 » C#从入门到精通 » 正文

方法和作用域(二)

创建时间:2013年08月29日 22:05  阅读次数:(2793)
分享到:
3.2 使用作用域;
通过前面的几个例子,我们知道可以在方法内部创建变量。这种变量的有效期(生存期)起始于它的定义位置,结束于方法结束的时候。换言之,在同一个方法内,后续的语句都可以使用这些变量(变量在创建之后才能使用)。方法执行完毕后,这些变量也会随之消失。

假如一个变量能在程序中的一个特定位置使用,就说该变量“在那个位置的作用域中”或者说“在那个位置的范围中”。也就是说,一个变量的作用域或范围(scope)是指该变量能发挥作用的一个程序区域。除了变量有作用域,方法也有作用域。一个标识符(不管它代表变量还是方法)的作用域始于声明该标识符的那个位置。

3.2.1 定义局部作用域
界定方法主体的{与}定义了一个作用域。方法主体中声明的任何变量都具有那个方法的作用域;一旦方法结束,它们也会随之消失。另外,它们只能由那个方法内部的代码访问。这种变量称为局部变量(local variable),因为它们局限于声明它们的那个方法,不在其他任何方法的范围中。换言之,不能利用局部变量在不同的方法之间共享信息。例如:
class Example
{
void firstMethod()
{
int myVar;
...
}
void anotherMethod()
{
myVar = 42; // 错误–变量越界(变量不在当前方法的作用域中)
...
}
}

上述代码无法编译,因为anotherMethod方法试图使用一个不在它的作用域内的myVar变量。myVar变量只供firstMethod方法中的语句使用,而且那些语句要在声明myVar变量的那一行代码之后。

3.2.2 定义类的作用域
界定类主体的{和}也定义了一个作用域。在类的主体中(但不能在一个方法中)声明的任何变量都具有那个类的作用域。在C#术语中,开发者使用字段(field)一词来描述由一个类定义的变量。和局部变量相反,可以使用字段在不同的方法之间共享信息。例如:
class Example
{
void firstMethod()
{
myField = 42; // ok
...
}
void anotherMethod()
{
myField = 42; // ok
...
}
int myField = 0;
}

变量myField是在类的内部定义的,而且它在firstMethod和anotherMethod方法的外部。所以,myField具有类的作用域,可由类中的所有方法使用。

这个例子中还需注意一点。在方法中,一个变量必须先声明再使用。但字段稍有不同,一个字段可以在类的任何位置定义。从表面看,可以先在一个方法中使用一个字段,再在这个方法之后声明该字段——在这种情况下,编译器将为我们打点一切!

3.2.3 重载方法
如果两个标识符同名,而且是在同一个作用域中声明的,就可以说它们被重载(overloaded)了。通常,重载的标识符属于bug,会在编译时被捕捉到并报错。例如,假如我们在同一个方法中声明了两个同名的局部变量,就会得到一个编译时错误。类似地,假如在同一个类中声明了同名的两个字段,或者在同一个类中声明了两个完全一样的方法,就会得到一个编译时错误。这个事实表面上似乎不值一提,因为一切都会被报告为编译时错误。但是,确实有一个办法能真正地、不报错地重载标识符。这种重载不仅是有用的,而且是必要的。

以Console类的WriteLine方法为例,前面曾用该方法向屏幕输出一个字符串。然而,在“代码和文本编辑器”窗口中键入System.Console.WriteLine后,会自动弹出一个“智能感知”列表,其中列出了19个不同的版本!WriteLine方法的每个版本都获取一组不同的参数。其中有个版本不获取任何参数,只是输出一个空行;有个版本获取一个bool参数,并输出这个bool值的字符串形式(true或false);还有一个版本实现获取一个decimal参数,并以字符串的形式输出这个decimal值;等等。程序编译时,编译器会检查实际传递的实参的类型,然后调用参数集与之匹配的一个版本。下面是一个例子:
static void Main()
{
Console.WriteLine("The answer is ");
Console.WriteLine(42);
}

如果需要针对不同的数据类型执行相同的操作,重载就是一项十分有用的技术。如果一个方法有多个不同的实现,而且每个实现都有不同的参数集,就可以考虑重载该方法。这样一来,每个版本都有相同的方法名,但有不同的参数数量或者/以及不同的参数类型。利用这个功能,在调用一个方法时,可以提供一个以逗号分隔的实参列表,而编译器将根据这些实参的数量和类型来选择一个匹配的重载版本。但要注意,虽然可以重载一个方法的参数,但不能重载方法的返回类型。也就是说,不能声明只是返回类型有区别的两个方法(编译器虽然比较聪明,但还不至于聪明到那种程度)。

3.3 编写方法
我们将在下面的练习中创建一个方法,它用于计算一名顾问的收费金额,假定该顾问每天收取固定的费用。首先要制定应用程序的逻辑,然后利用“生成方法存根向导”写出符合这个逻辑的方法。接着,我们将在一个控制台应用程序中运行方法,以便对这个程序有一个印象。最后,将使用Visual Studio 2010调试器来检查方法调用。

开发应用程序逻辑
1. 在Visual Studio 2010中打开“文档”文件夹下的\Microsoft Press\Visual CSharp Step by Step\Chapter 3\DailyRate子文件夹中的DailyRate项目。
2. 在“解决方案资源管理器”中,双击Program.cs文件,在“代码和文本编辑器”窗口中显示代码。
3. 在run方法主体的一对大括号之间添加以下语句:
double dailyRate = readDouble("Enter your daily rate: ");
int noOfDays = readInt("Enter the number of days: ");
writeFee(calculateFee(dailyRate, noOfDays));

应用程序启动时,run方法将由Main方法调用(为了理解它的调用方式,需要对类有一定的理解,详情参见第7章)。 刚才在run方法中添加的代码块会调用readDouble方法(马上就要写这个方法),它要求用户输入顾问每天的收费金额。下一个语句调用readInt方法(也马上要写)来获取天数。最后将调用writeFee方法(马上就要写),以便在屏幕上显示结果。注意,传给writeFee的值是calculateFee方法(最后一个要写的方法)返回的值,后者获取每天的收费金额和天数,并计算要支付的总金额。

注意:由于尚未写好readDouble,readInt,writeFee和calculateFee方法,所以?智能感知?无法在输入上述代码的时候自动列出它们。另外,先不要试图生成程序,因为肯定会失败。

使用”生成方法存根向导”来编写方法
1. 在“代码和文本编辑器”窗口中,右击run方法中的readDouble方法调用。
随后将弹出一个快捷菜单,其中包含了用于生成和编辑代码的一些命令,如下图所示。


2. 在弹出的快捷菜单中,选择“生成”|“方法存根”命令。 随后,Visual Studio会检查对readDouble方法的调用,判断参数类型和返回值,并生成一个具有默认实现的方法,如下所示:
private double readDouble(string p)
{
throw new NotImplementedException();
}

新方法是使用一个private限定符来创建的,这方面的详情将在第7章讲述。方法主体目前只是抛出一个NotImplementedException异常(第6章将详细讨论异常)。我们将在下一步将主体替换成自己的代码。

3. 从readDouble方法中删除throw new NotImplementedException();语句,将它替换成以下代码:
Console.Write(p);
string line = Console.ReadLine();
return double.Parse(line);

上述代码块会将变量p中的字符串输出到屏幕。该变量是调用方法时传递的字符串参数,其中包含提示用户输入每日收费金额的一条消息。

注意:Console.Write方法与前几个练习中的Console.WriteLine方法非常类似,区别在于它不会在消息之后输出一个换行符。

用户输入一个值后,这个值会通过ReadLine方法读入一个字符串,并通过double.Parse方法转换为一个double值。结果作为方法调用的返回值来传回。

注意:ReadLine方法是与WriteLine配对的一个方法;它读取用户的键盘输入,并在用户按Enter键时结束读取。用户输入的文本会作一个String值返回。

4. 在run方法中,右击readInt方法调用,从弹出的快捷菜单中选择“生成方法存根(Stub)”。 随后会生成readInt方法,如下所示:
private int readInt(string p)
{
throw new NotImplementedException();

5. 将readInt方法主体中的throw new NotImplementedException();语句替换成以下代码:
Console.Write(p);
string line = Console.ReadLine();
return int.Parse(line);

这个代码块和readDouble方法非常相似。唯一的区别在于该方法返回一个int值,所以要用int.Parse方法将用户输入的字符串转换成数字。
6. 在run方法中,右击calculateFee方法调用,从弹出的快捷菜单中选择“生成方法存根(Stub)”。 随后会生成calculateFee方法,如下所示:
private object calculateFee(double dailyRate, int noOfDays)
{
throw new NotImplementedException();
}

注意:Visual Studio根据传入的实参来生成形参的名称。假如觉得不合适,你完全可以更改形参名称。更让人感兴趣的是方法的返回类型,目前是object。这表明Visual Studio无法根据当前的上下文来确定方法应该返回什么类型的值。object类型意味着可能返回任何“东西”;在方法中添加具体的代码时,应该把它修改成自己需要的类型。object类型的详情将在第7章讲述。

7. 修改calculateFee方法定义,使它返回一个double值,如下所示(注意加粗显示的部分):
private doublecalculateFee(double dailyRate, int noOfDays)
{
throw new NotImplementedException();
}

8. 将calculateFee方法的主体替换成以下语句,它计算两个参数值的乘积来获得需要支付的金额,并返回结果。
return dailyRate * noOfDays;

9. 右击run方法中的writeFee方法调用,从弹出的快捷菜单中选择“生成方法存根(Stub)”。注意,Visual Studio根据calculateFee方法的定义推断writeFee方法的参数应该是一个double。另外,由于方法调用没有使用一个返回值,所以方法的返回类型应该为void:
private void writeFee(double p)
{
...
}

提示:如果已经非常熟悉语法,也可以直接在?代码和文本编辑器?窗口中输入,并非一定要用?生成?菜单选项。
10. 在writeFee方法内部输入以下语句:
Console.WriteLine("The consultant's fee is: {0}", p * 1.1);

注意 这个版本的WriteLine方法演示了如何使用一个格式字符串。在WriteLine方法的第一个参数中(这是一个字符串),包含了一个{0}。这是一个占位符,会在运行时被替换成字符串后的表达式(P * 1.1)的值。相较于将表达式p * 1.1的值转换成字符串,再用+操作符把它连接到消息后面,这个技术显然更好一些。

11. 在“生成”菜单中,选择“生成解决方案”。
来源:.net学习网
说明:所有来源为 .net学习网的文章均为原创,如有转载,请在转载处标注本页地址,谢谢!
【编辑:Wyf】

打赏

取消

感谢您的支持,我会做的更好!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

最新评论

共有评论0条
  • 暂无任何评论,请留下您对本文章的看法,共同参入讨论!
发表评论:
留言人:
内  容:
请输入问题 99+39=? 的结果(结果是:138)
结  果: