第 I 部分 Microsoft Visual C#和Microsoft Visual Studio 2010 概述
第 1 章 欢迎进入 C#编程世界
第 2 章 使用变量、操作符和表达式
第 3 章 编写方法和应用作用域
第 4 章 使用决策语句
第 5 章 使用复合赋值和迭代语句
第 6 章 管理错误和异常
参考文献:Visual C# 2010 Step by Step
第1章 欢迎进入C#编程世界
本章旨在教会你:
l 使用Visual Studio 2010编程环境
l 创建C#控制台应用程序
l 使用命名空间
l 创建一个简单的C#图形应用程序
1、 重要提示 C#是一种区分大小写的语言。拼写Main时,首字母必须大写。
2、 命名空间(namespace)就是为了解决这个问题而设计的,它能为其他标识符(如类名)创建一个具名的容器。同名的两个类如果在不同的命名空间中,相互之间是不会混淆的。
3、 using语句限定了要使用的命名空间。在同一个文件中,在(这些using语句)后面的代码中,不再需要用所属的命名空间来显式限定每一个对象。
4、 using语句指出:以后要使用的名称来源于一个命名空间,在代码中不必对类名进行完全限定4。类被编译到程序集(assembly)中。程序集是一种文件,通常使用.dll扩展名。不过,严格地说,带有.exe扩展名的可执行文件也是程序集。
第1章快速参考 目 标 |
操 作 |
使用Visual Studio 2010 标准版或专业版创建一个新的控 | 选择“文件”|“新建”|“项目”,从而打开“新建项目”对话框。在左边的“已安装的模板”窗 |
制台应用程序 | 格中选择“Visual C#”,在中间的窗格中选择“控制台应用程序”。在“位置”框中为项目文件选择一个目录。为项目输入一个名称。单击“确定”。 |
使用Visual C# 2010学习版创建一个新的控制台应用程序 | 选择“文件”|“新建项目”,从而打开“新建项目”对话框。模板选择“控制台应用程序”。为项目输入一个名称。单击“确定”。 |
用Visual Studio 2010标准版或专业版创建一个新的图形化应用程序 | 选择“文件”|“新建”|“项目”,从而打开“新建项目”对话框。在左边的“已安装的模板”窗格中选择“Visual C#”,在中间的窗格中选择“WPF应用程序”。在“位置”框中为项目文件选择一个目录。为项目输入一个名称。单击“确定”。 |
使用Visual C# 2010学习版创建一个新的图形化应用程序 | 选择“文件”|“新建项目”,从而打开“新建项目”对话框。模板选择“WPF应用程序”。为项目输入一个名称。单击“确定”。 |
生成应用程序 | 选择“生成”|“生成解决方案” |
运行应用程序 | 选择“调试”|“开始运行(不调试)” |
第2章 使用变量、操作符和表达式
本章旨在教会你:
l 理解语句、标识符和关键字
l 使用变量来存储信息
l 使用基本数据类型
l 使用+和-及其他算术操作符
l 对变量进行递增和递减
1、 语句(statement)是能执行一个操作的命令。我们组合使用各种语句来创建方法。第3章将进一步介绍方法。目前,请暂时将方法(method)视为一个具名的语句序列。第1章介绍过的Main就是方法的一个例子。C#语句遵循一个良好定义的规则集。这些规则对语句的格式和构成进行了描述,我们将这些规则统称为语法(syntax);对应地,规定一个语句应该做什么的规范统称为语义(semantic)。
2、 C#是一种‚自由格式‛(free format)的语言;这意味着所有空白(如空格字符或者换行符)仅充当分隔符而已,除此之外毫无意义。
3、 重要提示 C# 是一种对大小写敏感的语言,例如,footballTeam和FootballTeam是两个不同的标识符。
4、 C#语言保留了77个标识符供自己使用,程序员不可出于自己的目的而重用这些标识符。这些标识符称为关键字(keyword),每个关键字都有特定的含义。
5、 命名变量
为避免混淆,应该为变量采用一个命名规范。下面给出了一些常规建议。
l 名称不要以下划线开头。
l 不要创建仅仅大小写有别的标识符。例如,不要在创建了一个名为myVariable的变量之后又创建一个名为MyVariable的变量,并同时使用这两个变量,它们很容易混淆。
注意使用仅大小写有别的标识符,在使用其他语言开发的应用程序中,就不好重用类,因为那些语言可能是不区分大小写的,如Visual Basic。
l 名称以小写字母开头
l 在包含多个单词的标识符中,从第二个单词开始,每个单词都采取首字母大写的形式(这称为camelCase记号法)。
l 不要使用匈牙利记号法(Hungarian notation)。阅读本书的Microsoft Visual C++开发人员或许会熟悉这种记号法。但是,假如不知道匈牙利记号法是什么,也不必深究。
6、 注意 Microsoft Visual Basic程序员请注意,C#不允许隐式声明。所有变量在使用前都必须显式地声明。
7、 C#不允许使用未赋值的变量。变量必须先赋值再使用,否则程序可能无法编译。这就是所谓的明确赋值规则(Definite Assignment Rule)。例如,以下语句将产生一个编译时错误,因为age尚未赋值:
int age;
Console.WriteLine(age); // 编译时错误
8、.NET Framework 的每个数据类型都有 ToString 方法,用于将对象转换成字符串形式。
9、+操作符可用于连接字符串值
10、在 C#中,带小数点的文字常量数字肯定是 double 值,而不是 float 值,目的是保留尽可能高的精度。
11、C#还支持你或许不太熟悉的一个算术操作符,即取模(余数)操作符。它用百分号(%)表示。 x % y 结果就是用 x 除以 y 所得的余数。例如,9 % 2 结果是 1,因为 9 除以 2,结果是 4 余 1。
注意 如果熟悉 C 和 C++, 就知道不能在这两种语言中对 float 和 double 类型的值使用取模操作符。但C#允许这样做。取模操作符适用于所有数值类型,而且结果不一定为整数。
例如,表达式 7.0 % 2.4 结果是 2.2。
12、数值类型和无穷大
C#语言中的数字,还有另外两个特性是你必须了解的。例如,任何数除以 0 所得的结果是无穷大,不在 int,long 和 decimal 类型的范围内。所以,计算 5 / 0 之类的表达式会出错。但是,double 和 float 类型实际上有一个可以表示无穷大的特殊值,因此表达式 5.0 / 0.0 的值是 Infinity(无穷大)。这个规则的唯一例外是表达式 0.0 / 0.0 的值。通常,如果 0 除以任何数,结果都为 0,但如果用任何数除以 0,结果就为无穷大。表达式 0.0 / 0.0 会陷入一种自相矛盾的境地:值既为 0,又无穷大。针对这种情况,C#语言提供了另一个值 NaN,即“not a number”。所以,如果计算表达式 0.0 /0.0,则结果为 NaN。 NaN 和 Infinity 可在表达式中使用。计算 10 + NaN,结果为 NaN。计算 10 + Infinity,结果为 Infinity。规则的唯一例外是Infinity * 0,其结果为 0。而NaN * 0 的结果仍为 NaN。
6、记住文本框的 Text 属性包含字符串,所以必须先将字符串转换为整数,然后才能赋给 int 变量。int 数据类型提供了int.Parse 方法来执行这个转换
7、在现实生活中,54 / 13 的结果应该是4.153846…(如此重复)。但这不是现实生活;这是C#!正如前面解释的,在 C#中整数除以整数结果也是整数。
13、新手 C#程序员易犯的错误是试图将赋值操作符的这种用法与变量声明一起使用,
例如:int myInt, myInt2, myInt3 = 10;
上述 C#代码在语法上没有错误(能通过编译),但它做的事情可能跟你想象的不同。它实际是声明变量 myInt,myInt2 和 myInt3,并将 myInt3 初始化为 10。然而,不会初始化 myInt或者 myInt2。如果尝试在以下表达式中使用 myInt 或者 myInt2:
myInt3 = myInt / myInt2;
编译器会报告以下错误:
使用了未赋值的局部变量 "myInt"
使用了未赋值的局部变量 "myInt2"
14、count++返回递增发生前的count 值,++count 返回递增发生后的 count 值。例如:
int x;
x = 42;
Console.WriteLine(x++); // 执行这个语句后,x 等于 43,但控制台上输出的是 42
x = 42;
Console.WriteLine(++x); // 执行这个语句后,x 等于 43,控制台上输出的也是 43
其实很好记,只需看表达式各个元素(操作符和操作数)的顺序即可。在表达式 x++中,变量
x 首先出现,所以先返回它现在的值,然后再递增;在表达式++x 中,++操作符首先出现,
所以先对 x 进行递增,再将新值作为表达式的值返回
15、为此,只需用 var 关键字代替类型名称,如下所示:
var myVariable = 99;
var myOtherVariable = "Hello";
两个变量 myVariable 和 myOtherVariable 称为隐式类型变量。var 关键字告诉编译器根据用于初始化变量的表达式推断变量类型。在本例中,myVariable 是 int 类型,而 myOtherVariable是 string 类型。必须注意,var 只是在声明变量时提供一些方便。但变量一经声明,就只能将编译器推断的那种类型的值赋给它。例如,不能再将 float, double, string 值赋给 myVariable。还要注意,只有提供表达式来初始化变量,才能使用关键字 var。以下声明非法,会导致编译错误:
var yetAnotherVariable; // 错误 - 编译器不能推断类型
第 2 章快速参考
目标 操作
声明变量 按顺序写数据类型名称、变量名和分号,示例如下:
int outcome;
声明并初始化变量 按顺序写数据类型名称、变量名、赋值操作符、初始值和分号,示例如下:
int outcome = 99;
更改变量值 按顺序写变量名、赋值操作符、用于计算新值的表达式和分号,示例如下:
outcome = 42;
生成变量值的字符串形式 调用变量的ToString 方法,示例如下:
int intVar = 42;
string stringVar = intVar.ToString();
将 string 转换成 int 调用System.Int32.Parse 方法。示例如下:
string stringVar = "42";
int intVar = System.Int32.Parse(stringVar);
覆盖操作符优先级 在表达式中使用圆括号强制求值顺序,示例如下:
(3 + 4) * 5
将多个变量初始化为同一个值 使用赋值语句初始化所有变量,示例如下:
myInt4 = myInt3 = myInt2 = myInt = 10;
递增或递减变量 使用++或--操作符,示例如下:
count++;
第 3 章 方法和作用域
本章旨在教会你:
l 声明和调用方法
l 向方法传递数据
l 从方法返回数据
l 定义局部作用域和类作用域
l 使用集成调试器逐语句和逐过程调试方法
1、重要提示 C, C++和 MicrosoftVisual Basic 程序员请注意,C#不支持全局方法。所有方法必须在类的内部,否则代码无法编译。
2、注意 必须显式指定参数类型和方法返回类型。不能使用 var 关键字。
3、使用“生成方法存根向导”编写方法
4、注意 Console.Write 方法与前几个练习中的 Console.WriteLine 方法很相似,区别在于最后不输出换行符
5、注意 ReadLine 方法是与 WriteLine 配对的方法;它读取用户的键盘输入,并在用户按Enter 键时结束读取。用户输入的文本作为 String 值返回
6、注意,Visual Studio 根据传递的实参来生成形参名称。假如觉得不合适,完全可以更改形参名称。更让人感兴趣的是方法的返回类型,目前是 object。这表明 Visual Studio 无法根据当前上下文判断方法返回什么类型的值。object 类型意味着可能返回任何“对象”;在方法中添加具体的代码时,应把它修改成自己需要的类型。object 类型的详情将在第7 章讲述
7、注意 这个版本的 WriteLine 方法演示了如何使用格式字符串。方法的第一个参数(字符串),包含{0}。这称为占位符,会在运行时替换成字符串后的表达式(P * 1.1)的值。相较于将达式 p *1.1 的值转换成字符串,再用+操作符把它连接到字符串后面,这个技术显然更好。
8、重构代码
Visual Studio 2012 非常有用的一个功能就是重构代码。有时要在应用程序的多个位置写相同(或非常相似)的代码。这时可右击代码,从弹出菜单中选择“重构”|“提取方法”。随后出现“提取方法”对话框,提示输入用于包含代码的方法的名称。输入方法名并单击“确定”。随后会创建方法,并将代码转移到其中,而代码原来的位置被替换成对新方法的调用。“提取方法”还有一定的智能,可判断方法是否应该获取参数,以及是否应该返回值。
9、两个标识符同名,而且在同一个作用域中声明,就说它们被重载(overloaded)。如果要针对不同数据类型或者不同信息组别执行相同的操作,重载是一项十分有用的技术。如果方法多个实现,每个实现都有不同的参数集,就可重载该方法。
但要注意,虽可重载方法的参数,但不能重载方法的返回类型。也就是说,不能声明仅返回类型有别的两个方法(编译器虽然比较聪明,但还没有聪明到那种程度)。
10、提示 按 F11 键等同于单击“调试”工具栏的“逐语句”按钮。
单击“调试”工具栏中的“逐语句”按钮。
这会导致调试器跳入正在调用的方法。黄色箭头指向 readDouble 方法的起始大括号。
11、提示 按 F10 键等同于单击“调试”工具栏的“逐过程”按钮。
在“调试”工具栏中单击“逐过程”按钮。
这会导致方法执行下一个语句而不调试它。如果要调用方法,但不想跑到方法中单步调试其中每个语句,就可采取这个操作。
12、提示 按 Shift + F11 组合键等同于单击“调试”工具栏的“跳出”按钮。
在“调试”工具栏中单击“跳出”按钮。
这个操作会导致方法在不被打断的前提下一直执行到末尾。
13、定义可选参数
为了指定可选参数,可在定义方法时使用赋值操作符为该参数提供默认值。以下 optMethod方法的第一个参数是必须的,因为它没有提供默认值,但第二个和第三个参数可选:
void optMethod(int first, double second =0.0, string third = "Hello")
{
...
}
可选参数只能放在必须参数之后。含可选参数的方法在调用方式上与其他方法无异。都是指定方法名,提供任何必须的参数(实参)。区别在于,与可选参数对应的实参可以省略,方法运行时会为省略的实参使用默认值。
14、传递具名参数
C#默认根据每个实参在方法调用中的位置判断对应形参。所以在上一节的第二个示例方法调用中,两个实参分别传给 optMethod 方法的 first 和 second 形参,因为它们在方法声明中的顺序如此。C#还允许按名称指定参数。这样就可按照不同顺序传递实参。要将实参作为具名参数传递,必须输入参数名,一个冒号,然后是要传递的值。
第 3 章快速参考
目标 操作
声明方法 在类内部写方法。指定方法名,参数列表和返回类型。后面是一对大括
号中的方法主体。示例如下:
int addValues(int leftHandSide, intrightHandSide)
{
...
}
从方法内部返回值 在方法内部写 return语句。示例如下:
return leftHandSide + rightHandSide;
不从方法返回数据 使用单独的 return 语句:
return;
调用方法 写方法名,在圆括号中添加必要的实参。例如:
addValues(39, 3);
使用“生成方法存根向导” 右击方法调用,从弹出菜单中选择“生成”|“方法存根”
显示“调试”工具栏 选择“视图”|“工具栏”,勾选“调试”
跳入 方 法 并 逐 语 句 调 试
(Step into)
单击“调试”工具栏中的“逐语句”按钮,或者从菜单栏选择“调试”|
“逐语句” ,或者按 F11 键
跳出方法,忽略对方法中的
其他语句的调试,一路执行
到方法尾(Step out)
单击“调试”工具栏中的“跳出”按钮,或者从菜单栏选择“调试”| “跳
出” ,或者按Shift+F11 组合键
直接执行方法而不对其进
行调试(Step over)
单击“调试”工具栏中的“逐过程”按钮,或者从菜单栏中选择“调试”
|“逐过程” ,或者按 F10 键
为方法指定可选参数 在方法声明中为参数提供默认值。示例如下:
void optMethod(int first, double second =0.0,
string third = "Hello")
{
...
}
利用具名参数向方法提供
实参
在方法调用中指定参数名。示例如下:
optMethod(first : 100, third :"World");
第 4 章 使用判断语句
本章旨在教会你:
l 声明布尔变量
l 使用布尔操作符创建结果为true 或 false 的表达式
l 使用 if 语句,依据布尔表达式的结果做出判断
l 使用 switch 语句做出更复杂的判断
1、这时可将嵌套 if 语句改写成 switch 语句,提高程序效率并增强可读性。
2、拜托,只用布尔表达式!
if 语句中的表达式必须放在一对圆括号中。除此之外,表达式必须是布尔表达式。在另一些语言中(尤其是 C和 C++),还可以使用整数表达式,编译器自动将整数值转换成 true(非 0值)或 false(0)。C#不允许这样做。对于这样的表达式,编译器会报错。如果在 if 语句中不慎写了赋值表达式,而不是执行相等性测试,C#编译器也能识别出这个错误。
例如:
int seconds;
...
if (seconds = 59) // 编译时错误
...
if (seconds == 59) // 正确
在本来该用==的地方用了=,是 C/C++程序容易出现 bug 的另一个原因。在 C 和 C++中,会将所赋的值(59)悄悄地转换成布尔值(任何非 0 的值都被视为 true),造成每次都执行 if 语句之后的代码。另外,布尔变量可以作为 if 语句的表达式使用,但必须包含在一对圆括号中:
bool inWord;
...
if (inWord == true) // 可以这样写,但不常用
...
if (inWord) // 更好的写法
3、重要提示 省略大括号将造成两个严重后果。首先, C#编译器只将第一个语句(seconds=0)
与 if 语句关联,下一个语句(minutes++)不再成为 if 语句的一部分。其次,当编译器遇到 else 关键字时,不会将它与前一个 if 语句关联,所以会报告一个语法错误。因此,一个好习惯是 if语句的每个分支都用代码块定义,即使代码块只包含一个语句。以后添加代码会更省心。
4、注意 在 C#语言和 XML 中,单引号(')和双引号(")有特殊含义,分别用于界定字符和字
符串常量。
第 4 章快速参考
目标 操作 示例
判断两个值是否相等 使用操作符==或!=
answer == 42
比较两个表达式的值 使用操作符<,<=,>或>=
age >= 21
声明布尔变量 声明 bool 类型的变量
bool inRange;
创建布尔表达式,只在两个条件
都为 true,表达式才为 true
使用操作符&&
inRange = (lo <= number)
&& (number <= hi);
创建布尔表达式,只要两个条件
的任何一个为 true,表达式就为
true
使用操作符||
outOfRange = (number < lo)
|| (hi < number);
条件为 true 时运行一个语句使用 if 语句
if (inRange)
process();
条件为 true 时运行多个语句 使用 if 语句和代码块
if (seconds == 59)
{
seconds = 0;
minutes++;
}
将不同语句与控制表达式的不
同值关联
使用 switch 语句
switch (current)
{
case 0:
...
break;
case 1:
...
break;
default :
...
break;
}
第 5 章 使用复合赋值和循环语句
本章旨在教会你:
l 使用复合赋值操作符更新变量值
l 使用 while、for 和 do 循环语句
l 单步执行 do 语句,观察变量值的变化
1、在变量上加一个值是常见操作,所以C#专门提供了+=操作符来简化它。要在 answer 上加 42,有经验的程序员会这样写:
answer+= 42;
类似地,可将任何算术操作符与赋值操作符合并,下表进行了总结。这些操作符称为复合赋值操作符。
操作符+=可用于字符串;作用是将一个字符串附加到另一个字符串末尾。
2、break 和 continue 语句
第 4 章用 break 语句跳出 switch 语句。还可用它跳出循环。执行 break 后,系统立即终止循环,并从循环之后的第一个语句继续执行。在这种情况下,循环的“更新”和“继续”条件都不会重新判断。相反, continue 语句造成当前循环结束,立即开始下一次循环(在对布尔表达式重新求值之后)。下面是在控制台上输出 0~9 的例子的另一个版本,这次使用 break 和continue 语句:
int i =0;
while(true)
{
Console.WriteLine("continue" + i);
i++;
if (i< 10)
continue;
else
break;
}
这段代码看起来让人难受。在许多编程守则中,都建议谨慎使用 continue 语句,或者根本不要使用,因为它很容易造成难以理解的代码。continue 语句的行为还令人捉摸不定。例如,在 for 语句中执行 continue 语句,会在运行 for 语句的“更新(控制变量)”部分之后,才开始下一次循环。
3、在 Windows 操作系统常用的字符集中,字符'0'的代码是整数值 48。字符'1'的代
码是 49,字符'2'的代码是 50,以此类推,直到字符'9',它的代码是 57。C#允许将字符
当作整数处理,允许对它们执行算术运算。
第 5 章快速参考
目标 操作
在变量(variable)上加一个值(amount)使用复合加法操作符。示例如下:
variable+= amount;
从变量(variable)中减一个值(amount) 使用复合减法操作符。示例如下:
variable-= amount;
条件为 true 时运行一个或多个语句 使用 while 语句。示例如下:
int i =0;
while (i< 10)
{
Console.WriteLine(i);
i++;
}
条件为 true 时运行一个或多个语句 还可使用 for 语句。示例如下:
for (inti = 0; i < 10; i++)
{
Console.WriteLine(i);
}
一次或反复多次执行语句 使用 do 语句。示例如下:
int i =0;
do
{
Console.WriteLine(i);
i++;
}
while (i< 10);
CHAPTER 6
Managing errors and exceptions
Aftercompleting this chapter, you will be able to
■ Handle exceptionsby using the try, catch, and finally statements.
■ Control integeroverflow by using the checked and unchecked keywords.
■ Raise exceptionsfrom your own methods by using the throw keyword.
■ Ensure that codealways runs, even after an exception has occurred, by using a finally block
1、 由内向外遍历了所有调用方法,“运行时”找到一个相匹配的catch处理程序,整个程序,若找不到,整个程序就会终止,并报告一个未处理的异常。
2、 一个异常发生后,将运行由“运行时”发现的第一个匹配的异常处理程序,其他处理程序被忽略。
3、 重要提示 只有直接放在checked块中的整数运算才会得到检查。例如,假如块中出现一个方法调用,就不会对调用的方法中的整数运算进行检查。
4、 Checked语句是指以checked关键字开头的一个代码块。Checked语句中的任何整数运算一处,都会派出一个OverflowException异常。
5、 还可以使用unchecked关键字来创建一个强制不检查溢出的语句块。一个unchecked块中的所有整数运算都不会检查,永远不会抛出OverflowException异常。
6、 不能使用checked和unchecked关键字来控制浮点(非整数)运算。Checked和unchecked关键字只能适用于对int和long等整型执行的运算。浮点运算永远不会抛出OverflowException——即使让一个浮点数除以0.0(.NET Framwork有专门表示无穷大的机制)。
7、 几种异常
private void calculateClick(object sender, RoutedEventArgs e)
{
try
{
int leftHandSide =int.Parse(lhsOperand.Text);
int rightHandSide =int.Parse(rhsOperand.Text);
int answer = doCalculation(leftHandSide,rightHandSide);
result.Text =answer.ToString();
}
catch (FormatException fEx) //这个catch处理程序将捕捉由int.Parse抛出的FormatException,并将它的Mesaage文本写入窗体底部的result文本框的Text属性中。
{
result.Text =fEx.Message;
}
catch (OverflowException oEx) //用checked()函数实现对整数溢出检查,如果发生溢出,将抛出一个OverflowException异常。
{
result.Text =oEx.Message;
}
catch (InvalidOperationException ioEx) //这段代码捕捉InvalidOperationException异常。如果用户没有选择任何任何操作符按钮,单击Calculate按钮就会抛出此异常。并在出现异常的地方用throw语句抛出一个异常对象。
{
result.Text =ioEx.Message;
}
catch (Exception ex) //但还有可能发生的异常未被捕捉,它们会造成应用程序执行失败。例如,除以0。因此,可以添加一个常规catch处理程序来捕捉Exception。这样就可以捕捉一切未处理的异常。
{
result.Text =ex.Message;
}
}
//finally块确保关键代码总是得以执行,即使发生了异常。finally块要么紧接在try块之后,要么紧接在try块之后的最后一个catch处理程序之后。只要程序进入一个finally块关联的try块,则finally块始终都会运行,即使发生了一个异常。