语句

什么是语句


  • 语句是描述某个类型或让程序执行某动作的源代码指令。
  • 语句主要有3类
    • 声明语句 声明类型或变量
    • 嵌入语句 执行动作或管理控制流
    • 标签语句 控制跳转

例:语句示例

int x=;       //简单声明
int z; //简单声明
{ //块
int y=; //简单声明
z=x+y; //嵌入语句
top:y=; //标签语句
...
{ //嵌套块
...
} //结束嵌套块
} //结束外部块

块在语法上算作单条嵌入语句。任何语法上需要一个嵌入语句的地方,都可以使用块。

空语句仅由一个分号组成。当程序逻辑不需要任何动作时使用。

if(x<y)
;
else
z=a+b;

控制流语句


C#提供与现代编程语言相同的控制流结构。

  • 条件执行依据一个条件执行或跳过一个代码片段。条件执行语句如下:

    • if
    • if…else
    • switch
  • 循环语句重复执行一个代码片段。循环语句如下:
    • while
    • do
    • for
    • foreach
  • 跳转语句把控制流从一个代码片段改变到另一个代码片段中的指定语句。跳转语句如下:
    • break
    • continue
    • return
    • goto
    • throw

条件执行和循环结构(foreach)需要一个测试表达式或条件以决定程序应当在哪里继续执行。

与C和C++不同,测试表达式必须返回bool型值。数字在C#中没有布尔意义。

if语句


if语句实现按条件执行。if语句的语法如下所示。

  • TestExpr必须计算成bool型值
  • 如果TestExpr求值为true,执行Statement
  • 如果求值为false,则跳过Statement
if(TestExpr)
Statement

例:if语句示例

//With a simple statement
if(x<)
z=x-;//简单语句不需要大括号
//With a block
if(x>=)
{ //块需要大括号
x-=;
y=x+z;
}
int x=;
if(x) //错:表达式必须是bool型,而不是int型
{
...
}

if…else语句


if…else语句实现双路分支。if…else语句的语法如下。

  • 如果TestExpr求值为true,执行Statement1
  • 如果求值为flase,执行Statement2
if(TestExpr)
Statement1
else
Statement2


例:if…else示例

if(x<=)
z=x-; //简单语句
else
{ //多条语句组成的语句块
x-=;
y=x+z;
}

if…else或if语句都可以嵌套。如果你在阅读嵌套if…else语句代码时,要找出else属于哪个if,有个简单的规则。每个else都属于离它最近的前一条没有相关else字句的if语句。

while循环


while是一种简单循环结构,其测试表达式在循环的顶部执行。while循环语法如下。

  • 首先对TestExpr求值
  • 如果TestExpr求值为false,将继续执行在while循环结尾之后的语句
  • 当TestExpr求值为true时,执行Statement,并且再次对TestExpr求值。每次TestExpr求值为true时,Statement都要在执行一次。循环在TestExpr求值为false时结束
while(TestExpr)
Statement


例:while循环示例

int x=;
while(x>)
{
Console.WriteLine("x:{0}",x);
x--;
}
Console.WriteLine("Out of loop");

do循环


do循环是一种简单循环结构,其测试表达式在循环的底部执行。do循环语法如下。

  • 首先,执行Statement
  • 然后,对TestExpr求值
  • 如果TestExpr返回true,那么再次执行Statement
  • 每次TestExpr返回true,都将再次执行Statement
  • 当TestExpr返回false时,控制传递到循环结构结尾之后的那条语句

do循环有几个特征,使它与其他控制流结构相区分。

  • 循环体Statement至少执行一次,即使TestExpr初始为false,这是因为在循环底部才会对TestExpr求值
  • 在测试表达式的关闭圆括号之后需要一个分号
do
Statement
while(TestExpr); //结束do循环


例:do循环示例

int x=;
do
Console.WriteLine("x is {0}",x++);
while(x<);

for循环


只要测试表达式在循环体顶端计算时返回true,for循环结构就会执行循环体。for循环语法如下。

  • 在for循环的开始,执行一次Initializer
  • 然后对TestExpr求值
  • 如果它返回true,执行Statement,接着是IterationExpr
  • 然后控制回到循环顶端,再次对TestExpr求值
  • 只要TestExpr返回true,Statement和IterationExpr都将被执行
  • 一旦TestExpr返回false,就继续执行Statement之后的语句
for(Initializer;TestExpr;IterationExpr)
Statement

语句中的一些部分是可选的,其他部分是必需的。

  • Initializer、TestExpr和IterationExpr都是可选的。它们位置可以空着。如果TestExpr位置是空的,那么测试被假定返回true。因此程序要避免进入无限循环,必须有某种其他退出该语句的方法
  • 作为字段分隔符,两个分号是必需的,即使其他部分都省略了

  • Initializer只执行一次,在for结构的任何其他部分之前。它常用于声明和初始化循环中使用的本地变量
  • 对TestExpr求值以决定应该执行Statement还是跳过。它必须计算成bool类型的值。如前所述,如果TestExpr为空,将永远返回true
  • IterationExpr在Statement之后并且在返回到循环顶端TestExpr之前立即执行

例:for循环示例

for(int i=;i<;i++)
{
Console.WriteLine("Insile loop. i: {0}",i);
}
Console.WriteLine("Out of loop");

for语句中变量的作用域

任何声明在Initializer中的变量只在该for语句的内部可见。

  • 这与C和C++不同,C和C++中声明把变量引入到外围的块
  • 下面的代码阐明了这点
这里需要类型来声明

for(int i=;i<;i++) //变量i在作用域内
Statement; //语句
//在该语句之后,i不再存在
这里仍需要类型,因为前面的变量i已经超出存在范围

for(int i=;i<;i++) //我们需要定义一个新的i变量
Statement; //因为先前的i变量已经不存在

在循环体内部声明的变量只能在循环体内部使用。

循环变量常常使用标识符i、j、k。这是早年FORTRAN程序的传统。在FORTRAN中,以字母I、J、K、L、M、N开头的标识符默认为INTEGER类型,没有必要声明。由于循环变量通常为整型,程序员简单地使用I作为循环变量的名称,并把它作为一种约定。这简短易用,而且不用声明。如果存在嵌套循环,内层循环变量通常为J。如果还有内层嵌套循环,就用K。

初始化和迭代表达式中的多表达式

初始化表达式和迭代表达式都可以包含多个表达式,只要它们用逗号隔开。

例:下面代码,初始化表达式中有两个变量声明,迭代表达式中有两个表达式

class Program
{
static void Main()
{
const int MaxI=;
for(int i=,j=;i<MaxI;i++,j+=)
{
Console.WriteLine("{0},{1}",i,j);
}
}
}

switch语句


switch实现多路分支

  • switch包含0个或多个分支
  • 每个分支以一个或多个分支标签开始
  • 每个分支的末尾必须为break或其他4种跳转语句
    • 跳转语句包括 break、return、continue、goto、throw
    • break语句将执行过程跳转到switch的尾部

分支标签按顺序求值。如果某标签与测试表达式的值匹配,就执行该分支,然后跳到switch尾部


分支示例

例:下面代码通过for循环,执行switch语句5次

for(int x=;x<;x++)
{
switch(x)
{
case :
Console.WriteLine("x is {0} -- In Case 2",x);
break;
case :
Console.WriteLine("x is {0} -- In Case 5",x);
break;
default:
Console.WriteLine("x is {0} -- In Default case",x);
break;
}
}

switch语句补充

switch语句可以有任意多分支,包括没有分支。default不是必需的,然而通常认为拥有default是好习惯,因为它可以捕获潜在错误。
例:没有default

for(int x=;x<;x++)
{
switch(x)
{
case :
Console.WriteLine("x is {0} -- In Case 5",x);
break;
}
}

例:只有default

for(int x=;x<;x++)
{
switch(x)
{
default:
Console.WriteLine("x is {0} -- In Default Case",x);
break;
}
}

分支标签

case后的分支标签必须是常量表达式,即必须在编译时完全获取运算结果。

和C/C++不同,每个switch段,包括可选的default段,必须以一个跳转语句结尾。在C#中,不可以执行一个switch段中的代码然后直接执行接下来的部分。

尽管C#不允许从一个分支到另一个分支的导向,你仍然可以把多个分支标签附加到任意分支。

switch(x)
{
case :
case : //可接受的
...
break;
case :
y=x+;
case : //因为没有break,所以不可接受
...
}

跳转语句


控制流到达跳转语句时,程序执行被无条件转移到程序的另一部分。

  • break
  • continue
  • return
  • goto
  • throw

这一章阐述前4条语句,throw在第11章讨论

break语句


前面你已经看到break被用在switch中。它还能被用在下列语句中:

  • for
  • foreach
  • while
  • do

break导致执行调出最内层封装语句(innermost enclosing statement)

例:break+while

int x=;
while(true)
{
x++;
if(x>=)
break;
}

continue语句


continue语句导致程序执行转到下列类型循环的最内层封装语句的顶端:

  • while
  • do
  • for
  • foreach

例:下例for循环在前3次迭代,它遇到continue直接返回循环顶部,错过WriteLine语句

for(int x=;x<;x++)
{
if(x<)
continue;
Console.WriteLine("Value of x is {0}",x);
}

标签语句


标签语句格式:

Identifier:Statement

标签语句的执行如果标签不存在,仅执行Statement部分。

  • 给语句增加一个标签允许控制从代码其他部分转移到该语句
  • 标签语句只允许用在块内部
标签

标签有自己的声明空间,所以标签的标识符可以与本地变量或参数名重复。

{
int xyz=;
...
xyz:Console.WriteLine("No problem.");
}
标签语句作用域

标签语句作用域为:

  • 它声明所在的块
  • 任何嵌套在该内部的块

goto语句


goto语句无条件转移控制到一个标签语句。

goto Identifier;

bool thingsAreFine;
while(true)
{
thingsAreFine=GetNuclearReactorCondition();
if(thingsAreFine)
Console.WriteLine("Things are fine.");
else
goto NotSoGood;
}
NotSoGood:Console.WriteLine("We have a problem.");

goto语句必须在标签语句的作用域内。

  • goto语句可以跳到它本身所在块内的任何标签语句,或跳出到任何它被嵌套的块内的标签语句
  • goto语句不能跳入任何嵌套在该语句本身所在块内部的任何块

警告:使用goto语句是非常不好的,因为它会导致弱结构化的、难以调试和维护的代码。EdsgerDijkstra在1968年给Communication of the ACM写了一封信,标题为“Go To Statement Considerred Harmful”,是对计算机科学非常重要的贡献。它是最先发表的描述使用goto语句缺陷的文章之一。

goto语句在switch语句内部

goto case ConstantExpression;//跳转到指定case段
goto default; //跳转到default段

using语句


有些类型的非托管对象数量有限制或很耗费系统资源。在使用完它们后,尽可能快地释放它们是非常重要的。using语句有助于简化该过程并确保这些资源被适当的处置(dispose)。
资源是指一个实现了System.IDisposable接口的类或结构。接口在第15章详述,但简而言之,接口就是未实现的函数成员的集合,类和结构可以选择去实现。IDisposable接口含有单独一个名称为Dispose的方法。

使用资源的阶段如下图:

  • 分配资源
  • 使用资源
  • 处置资源

如果在正在使用资源的那部分代码中产生一个意外的运行时错误,那么处置资源的代码可能得不到执行。


using语句不同于using指令。using指令在第21章阐述。

资源的包装使用

using语句帮助减少意外的运行时错误带来的潜在问题,它整洁地包装了资源的使用。
有两种形式的using语句。第一种形式如下:

  • 圆括号内的代码分配资源
  • Statement是使用资源的代码
  • using语句隐式产生处置该资源的代码
using(ResourceType Identifier=Expression)//分配资源
{
Statement//使用资源
}

第二种形式放在异常处理的finally块中。

  • 分配资源
  • 把Statement放进try块
  • 创建资源的Dispose方法的调用,并把它放进finally块

using语句示例
  • TextWriter资源打开一个文本文件,并向文件写入一行
  • TextReader资源接着打开相同的文本文件,一行一行读取并显式它的内容
  • 在两种情况中,using语句确保调用对象的Dispose方法
  • 还要注意Main中using语句和开始两行的using指令之间的区别
using System;    //using指令,不是using语句
using System.IO; //using指令,不是using语句
namespace UsingStatement
{
class Program
{
static void Main()
{
//using语句
using(TextWriter tw=File.CreateText("Lincoln.txt"))
{
tw.WriteLine("Four score and seven years ago,...");
}
//using语句
using(TextReader tr=File.OpenText("Lincoln.txt"))
{
string InputString;
while(null!=(InputString=tr.ReadLine()))
{
Console.WriteLine(InputString);
}
}
}
}
}

多个资源和嵌套

using语句还可以用于相同类型的多个资源,资源声明用逗号隔开。

    只有一个类型       资源       资源
↓ ↓ ↓
using(ResourceType Id1=Expr1,Id2=Expr2,...)
{
EmbeddedStatement
}

例:

class Program
{
static void Main()
{
using(TextWriter tw1=File.CreateText("Lincoln.txt"),
tw2=File.CreateText("Franklin.txt"))
{
tw1.WriteLine("Four score and seven years ago,...");
tw2.WriteLine("Early to bed;Early to rise...");
}
...
}
}

using语句还可以嵌套。

using(TextWriter tw1=File.CreateText("Lincoln.txt"))
{
tw1.WriteLine("Four score and seven years ago,...");
using(TextWriter tw2=File.CreateText("Franklin.txt"))
{
tw2.WriteLine("Early to bed;Early to rise...");
}
}
using语句的另一种形式
关键字   资源
↓ ↓
using(Expression)
{
EmbeddedStatement//使用资源
}

这种形式中,资源在using语句前声明。

TextWriter tw=File.CreateText("Lincoln.txt");
using(tw)
{
tw.WriteLine("Four score and seven years ago,...");
}

虽然这种形式也能确保对资源的使用结束后总是调用Dispose方法,但它不能防止在using语句已经释放了它的非托管资源后使用该资源,导致了不一致的状态。因此它提供了较少的保护,不推荐使用。


其他语句


还有一些语句和语言的特征相关。这些语句在涉及相应特征的章节中阐述。

语句 描述 相关章节
checked,unchecked 控制溢出检查上下文 第16章
foreach 遍历一个集合的每个成员 第12章和第18章
try、throw、finally 处理异常 第22章
return 将控制返回到调用函数的成员,而且还能返回一个值 第5章
yield 用于迭代 第18章

C#图解教程 第九章 语句的更多相关文章

  1. Objective-C 基础教程第九章,内存管理

    目录 Object-C 基础教程第九章,内存管理 前言: 对象生命周期 引用计数 RetainCount1项目例子 对象所有权 访问方法中的保留和释放 自动释放 所有对象放入池中 自动释放池的销毁时间 ...

  2. python 教程 第九章、 类与面向对象

    第九章. 类与面向对象 1)    类 基本类/超类/父类被导出类或子类继承. Inheritance继承 Inheritance is based on attribute lookup in Py ...

  3. C# 图解教程 第一章 C#和.NET框架

    C#和.NET框架 在.NET之前.NET时代 .NET框架的组成大大改进的编程环境 编译成CIL编译成本机代码并执行CLR Common Language RuntimeCLI Common Lan ...

  4. Flask 教程 第九章:分页

    本文翻译自The Flask Mega-Tutorial Part IX: Pagination 这是Flask Mega-Tutorial系列的第九部分,我将告诉你如何对数据列表进行分页. 在第八章 ...

  5. C#图解教程第一章 C#和.NET框架

    1.1 在.NET之前 C#发音:see shap 1.1.1 20世纪90年代后期的Windows编程  20世纪90年代后期各语言缺点:   1.纯Win32 API不是面向对象的,而且工作量比M ...

  6. C# 图解教程 第二章 C#编程概述

    C#编程概述 一个简单的C#程序标识符关键字Main:程序的起始点从程序输出文本注释 C#编程概述 一个简单的C#程序 标识符 标识符是一种字符串,用来命名变量.方法.参数和许多后面将要阐述的其他程序 ...

  7. 图解HTTP第九章

    基于 HTTP 的功能追加协议 1>HTTP 的瓶颈有哪些: 2>消除 HTTP 瓶颈的 SPDY,缩短 Web 页面的加载时间 [1]SPDY 的设计与功能 [2]SPDY 消除 Web ...

  8. 实战SpringCloud响应式微服务系列教程(第九章)使用Spring WebFlux构建响应式RESTful服务

    本文为实战SpringCloud响应式微服务系列教程第九章,讲解使用Spring WebFlux构建响应式RESTful服务.建议没有之前基础的童鞋,先看之前的章节,章节目录放在文末. 从本节开始我们 ...

  9. D3.js的v5版本入门教程(第九章)——完整的柱状图

    D3.js的v5版本入门教程(第九章) 一个完整的柱状图应该包括的元素有——矩形.文字.坐标轴,现在,我们就来一一绘制它们,这章是前面几章的综合,这一章只有少量新的知识点,它们是 d3.scaleBa ...

随机推荐

  1. BZOJ 1845: [Cqoi2005] 三角形面积并 [计算几何 扫描线]

    1845: [Cqoi2005] 三角形面积并 Time Limit: 3 Sec  Memory Limit: 64 MBSubmit: 1151  Solved: 313[Submit][Stat ...

  2. 利用Lua读写本地文件

    缘由 今天在使用Lua编写脚本时,需要用到读写文件的操作,很久没有使用Lua了,特写下此文来备忘一下. 简介 Lua对文件的操作与C对文件的操作基本一致,不管是参数还是方法.Lua中可以直接通过全局方 ...

  3. Redis 学习(一) —— 安装、通用key操作命令

    一.Redis介绍 1.介绍 通常,在系统中,我们会把数据交由数据库来存储,但传统的数据库增删查改的性能较差,且比较复杂.根据 80/20 法则,百分之八十的业务访问集中在百分之二十的数据上.是否可以 ...

  4. web前端 ajax请求上传图片数据类型处理

    form: function (url, paras, files, success) {//上传的文件类型是MultipartFile[]//如果把需要的参数直接传入,则会请求错误//设置请求参数的 ...

  5. linux 添加静态路由

    Linux下静态路由修改命令方法一:添加路由route add -net 192.168.0.0/24 gw 192.168.0.1route add -host 192.168.1.1 dev 19 ...

  6. iOS 开发 atomic 与 nonatomic 区别

    atomic :  变量默认是有该有属性的,这个属性是为了保证在多线程的情况下,编译器会自动生成一些互斥加锁的代码,避免该变量的读写不同步的问题. nonatomic  : 如果该对象无需考虑多线程的 ...

  7. 使用mybatis插入自增主键ID的数据后返回自增的ID

    在开发中碰到用户注册的功能需要用到用户ID,但是用户ID是数据库自增生成的,这种情况上网查询后使用下面的方式配置mybatis的insert语句可以解决: <insert id="in ...

  8. 使用EL表达式调用java方法

    首先,新建一个类,类中写一个静态方法 public class PrivilegeUtils { public static Boolean checkPrivilegeByName(User use ...

  9. 共享表空间VS独立表空间

    基础概念:共享表空间 VS 独立表空间 [共享表空间] 又称为system tablespace系统表空间,a small set of data files (the ibdata files) . ...

  10. STL源码剖析之序列式容器

    最近由于找工作需要,准备深入学习一下STL源码,我看的是侯捷所著的<STL源码剖析>.之所以看这本书主要是由于我过去曾经接触过一些台湾人,我一直觉得台湾人非常不错(这里不涉及任何政治,仅限 ...