基础中的基础——流程控制语句

本来没有这一节的内容,后来考虑到既然是一个系列文章,那么就尽可能写的详细一些 , 本节参考了网上朋友所写的例子,为的是让更多小伙伴学习,提高,加薪,如有版权问题,请邮件我,我第一时间处理。

语句:是程序中的小指令,本节主要以流程控制语句为主要内容。

流程控制语句中最常用的三个是 选择语句(即条件语句)、循环语句和异常处理语句

流程控制语句分类:

类别       关键字

选择语句       if 、 else 、 switch 、 case

循环语句       do 、 for 、 foreach 、 in 、 while

跳转语句       break 、 continue 、 default 、 goto 、 return

异常处理语句     throw 、 try-catch 、 try-finally

检查和未检查语句   checked 、 unchecked

非保护和固定语句   unsafe 、 fixed

锁定语句       lock

1. 条件语句

1.1 if - else

if (expression){}else{}, 其中 expression 是一个布尔类型, true 则执行第一区块, false 则执行 else 部分,这个小伙伴们肯定是相当滴熟悉了

使用这个语句有几个要说明的地方:

A.如果 if 或 else 区块部分只有一行,可以省略 {} 区块符 , 例如 :

int a,b,c;

if(a==b)

c++;

else

c--;

B. 对于多个条件判断可以使用 if (expression){}else if(expression){}else{}

C. 可以使用 if else 嵌套

D. 尽量避免使用多重嵌套和连续使用 if

E. 多重嵌套和多次 if 的情况,推荐使用下面的 switch case 语句

1.2 switch - case

switch 语句是通过将控制传递给其内部的一个 case 语句来处理多个选择的流程控制语句。

switch 语句的基本结构:

switch (<testVar>)

{

case <comparisonVal1>:

< 如果 <testVar> 等于 <comparisonVal1> 时执行的语句 >

break;

case <comparisonVal2>:

< 如果 <testVar> 等于 <comparisonVal2> 时执行的语句 >

break;

……

case <comparisonValN>:

< 如果 <testVar> 等于 <comparisonValN> 时执行的语句 >

break;

default:

< 如果没有与 <testVar> 匹配的 <comparisonValX> 时执行的语句 >

break;

}

(1)   <testVar> 中的值与 case 语句中指定的每个 <comparisonValX> 值进行比较,如果有一个匹配,就执行为该匹配提供的语句。如果没有匹配,就执行 default 部分中的代码。执行完每个部分中的代码后,还须有一个 break 语句。在执行完一个 case 块后,再执行第二个 case 语句是非法的。

(2) break 语句将中断 switch 语句的执行,而执行该结构后面的语句。

(3) 还有另一种方法可以防止程序流程从一个 case 语句转到下一个 case 语句。可以使用 return 语句。也可以使用 goto 语句,因为 case 语句实际上是在 C# 代码中定义标签。

(4) 一个 case 语句处理完后,不能自由进入下一个 case 语句,但有一个例外。如果把多个 case 语句放(堆叠)在一起,其后加一行代码,实际上是一次检查多个条件。如果满足这些条件中的任何一个,就会执行代码,例如:

using System;
class SwitchTest
{
    static void Main()
    {
        int n = 2;
        switch(n)
        {
            case 1:
            case 2:
            case 3:
                Console.WriteLine("It's 1, 2, or 3.");
                break;
        default:
            Console.WriteLine("Not sure what it is.");
            break;
        }
    }
}

输出:

It's 1, 2, or 3.

每个 <comparisonValX> 都必须是一个常量。一种方法是提供字面值,另一种方式是使用常量。在这里使用常量可读性更好。

2.循环语句

使用循环语句可以让程序多次执行相同的代码或代码块,这些代码或代码块称为循环体。对于任何一个循环体来说,都应该提供一个跳出循环的条件,不同的循环语句提供不同的条件。

C# 语言中提供了以下 4 种循环语句:

· for

· foreach - in

· do - while

· while

2.1 for

for 语句通常用来让一条语句或一个语句块执行一定的次数。

for 语句的一般形式:

for ([initializers]; [expression]; [iterators])

{

statement

}

其中:

initializers 表示初始化循环计数器,如果有多个变量需要初始化,可用逗号隔开。

expression 是 bool 类型的表达式,用来测试循环是否终止。

iterators 表示增大或减少循环计数器的值。

statement 是需要循环执行的语句。

其执行流程为:

· 首先初始化 initializers 。

· 接着,检查 expression 。如果为 true ,执行 statement ,并重新计算循环计数器的值。如果为 false ,则退出循环。

· 返回上一步,继续执行。

因为对 expression 的测试是在循环体执行之前,所以 for 语句可执行 0 次或多次。

for 语句的所有表达式都是可选的;例如,下列语句用于写一个无限循环:

for (;;)

{

...

}

示例:

// for loop
using System;
class ForLoopTest
{
    static void Main()
    {
        for (int i = 1; i <= 5; i++)
        {
            Console.WriteLine(i);
        }
    }
}

输出:

1

2

3

4

5

2.2 for - in

foreach 语句为数组或对象集合中的每个元素执行一遍循环体。通常用来遍历某个集合,以获取所需信息,但不应用于更改集合内容以避免产生不可预知的副作用。

语法:

foreach (type identifier in expression)

{

staterment

}

其中:

type 表示 identifier 的类型。

identifier 表示集合元素的循环变量。

expression 表示对象集合或数组表达式。集合元素的类型必须可以转换成 identifier 的类型。

staterment 表示需要循环执行的语句。

对于数组或集合中的每个元素,循环体都将执行一次。遍历完所有的元素后,程序将退出 foreach 块,执行后面的语句。

(1) foreach 在数组中的使用

该语句提供一种简单、明了的方法来循环访问数组的元素。

例如,下面的代码创建一个名为 numbers 的数组,并用 foreach 语句循环访问该数组:

int[] numbers = { 4, 5, 6, 1, 2, 3, -2, -1, 0 };
foreach (int i in numbers)
{
    System.Console.WriteLine(i);
}

对于多维数组,使用嵌套的 for 循环可以更好地控制数组元素。

(2) foreach 在集合中的使用

当对集合使用 foreach 语句时,该集合必须满足一定的条件。

例如下面的 foreach 语句:

foreach (ItemType item in myCollection)

myCollection 必须满足下面的要求。

集合类型:

必须是 interface 、 class 或 struct 。

必须包括一个名叫 GetEnumerator 的实例方法,该方法返回一个类型,比如 Enumerator 。

类型 Enumerator (类或结构)必须包含:

一个名为 Current 的属性。类型为 ItemType 或可以转换成 ItemType 的类型。它的属性访问器返回集合中的当前元素。

一个名叫 MoveNext 的方法。该方法用于增加计数器的值,如果集合中的元素个数小于计数器的值,该方法返回 true ,否则返回 false 。

2.3 do - while

do 语句重复执行括在 {} 里的一个语句或语句块,直到指定的表达式为 false 时为止。

do 循环的结构如下:

do

{

statement

} while (expression);

其中:

expression 为 bool 类型的表达式,或者是可以隐式转换成 bool 类型的表达式,也可以是重载 true 和 false 操作符的类型的表达式。用来测试循环是否终止。

statement 是需要循环执行的语句。

do-while 结构先执行循体语句,然后判断 while 条件是否为 true 。如果为 true ,将循环执行;如果为 false ,则退出循环。因此 do-while 循环结构中的语句至少要执行一次。

while 语句后面的分号是必须的。

示例:下面示例中,只要变量 y 小于 5 , do 循环语句就开始执行。

using System;
public class TestDoWhile
{
    public static void Main ()
    {
        int x = 0;
        do
        {
            Console.WriteLine(x);
            x++;
        }
        while (x < 5);
    }
}

输出:

0

1

2

3

4

2.4 While

当 while 语句中的判断条件为 true 时,循环体将一直循环执行。

语法:

while (expression)

{

statement

}

其中:

expression 表示 bool 类型的表达式。用来测试循环是否终止。

statement 表示需要循环执行的语句。

while 语句和 do-while 语句不同, do-while 是先执行循环体再判断条件,而 while 是先判断条件。如果条件为 true ,则执行循环体,否则将跳过循环体,执行 while 块后面的代码。因此, while 语句中的循环体可能执行 0 次或多次。

在 while 循环体中,可以使用 break 、 goto 、 reture 或 throw 语句跳出循环。如果要跳转到下一次循环,可在循环体中使用 continue 语句。

示例:

using System;
class WhileTest
{
    static void Main()
    {
        int n = 1;
        while (n < 6)
        {
            Console.WriteLine("Current value of n is {0}", n);
            n++;
        }
    }
}

输出:

Current value of n is 1

Current value of n is 2

Current value of n is 3

Current value of n is 4

Current value of n is 5

3. 跳转语句

跳转语句用于从程序的一个地方把执行控制转移到另一个地方,每一条跳转语句的应用都会增加程序执行流程的分支。

C# 语言中可使用以下 4 种跳转语句:

· break

· continue

· goto

· return

3.1 break 语句

break 语句用于中止当前执行的循环或它所在的 switch 语句,把控制交给循环或 switch 结构后面的语句。

示例:

在此例中,条件语句包含一个应该从 1 计数到 100 的计数器;但 break 语句在计数达到 4 后终止循环。

using System;
class BreakTest
{
    static void Main()
    {
        for (int i = 1; i <= 100; i++)
        {
            if (i == 5)
            {
                break;
            }
            Console.WriteLine(i);
        }
    }
}

输出:

1

2

3

4

下面的示例演示 break 在 switch 语句中的用法。

// break and switch
using System;
class Switch
{
    static void Main()
    {
        Console.Write("Enter your selection (1, 2, or 3): ");
        string s = Console.ReadLine();
        int n = Int32.Parse(s);
        switch (n)
        {
            case 1:
                Console.WriteLine("Current value is {0}", 1);
                break;
            case 2:
                Console.WriteLine("Current value is {0}", 2);
                break;
            case 3:
                Console.WriteLine("Current value is {0}", 3);
                break;
            default:
                Console.WriteLine("Sorry, invalid selection.");
                break;
        }
    }
}

输入 1 ,则示例输出为:

Enter your selection (1, 2, or 3): 1

Current value is 1

如果输入 4 ,则输出为:

Enter your selection (1, 2, or 3): 4

Sorry, invalid selection.

3.2 continue 语句

在循环体中使用 continue 语句将结束当前的循环,而进入下一次的循环。

示例:在此示例中,计数器最初是从 1 到 10 进行计数,但通过将 continue 语句与表达式 (i < 9) 一起使用,跳过了 continue 与 for 循环体末尾之间的语句。

using System;
class ContinueTest
{
    static void Main()
    {
        for (int i = 1; i <= 10; i++)
        {
            if (i < 9)
            {
                continue;
            }
            Console.WriteLine(i);
        }
    }
}

输出:

9

10

3.3 goto 语句

goto 语句将程序控制直接交给标记的语句。有以下形式:

goto identifier;

goto case constant-expression;

goto default;

其中:

identifier 表示一个标签。

constant-expression 表示一个 switch-case 标签。

在第一种形式中, identifier 指定位于当前循环体中的标签,是一个与 goto 语句位于同一个循环体中的标签。

goto 语句的常用方法是在 switch 语句中,将控制转换传递到特定的 switch-case 标签或 default 标签。

有时也在多层嵌套的循环体中使用 goto 语句跳出多层循环。

如果在代码中声明了标签,但从未引用过它,编译时将出现警告信息。

示例:下面的示例演示了 goto 在 switch 语句中的使用。

using System;
class SwitchTest{
    static void Main()
    {
        Console.WriteLine("Coffee sizes: 1=Small 2=Medium 3=Large");
        Console.Write("Please enter your selection: ");
        string s = Console.ReadLine();
        int n = int.Parse(s);
        int cost = 0;
        switch (n)
        {
            case 1:
                cost += 25;
                break;
            case 2:
                cost += 25;
                goto case 1;
            case 3:
                cost += 50;
                goto case 1;
            default:
                Console.WriteLine("Invalid selection.");
                break;
        }
        if (cost != 0)
        {
            Console.WriteLine("Please insert {0} cents.", cost);
        }
        Console.WriteLine("Thank you for your business.");
    }
}

如果输入了 2 ,示例输出:

Coffee sizes: 1=Small 2=Medium 3=Large

Please enter your selection: 2

Please insert 50 cents.

Thank you for your business.

下面的示例演示了使用 goto 跳出嵌套循环。

// Nested search loops

using System;
public class GotoTest1{
    static void Main()
    {
        int x = 200, y = 4;
        int count = 0;
        string[,] array = new string[x, y];
        // Initialize the array:
        for (int i = 0; i < x; i++)
            for (int j = 0; j < y; j++)
                array[i, j] = (++count).ToString();
        // Read input:
        Console.Write("Enter the number to search for: ");
        // Input a string:
        string myNumber = Console.ReadLine();
        // Search:
        for (int i = 0; i < x; i++)
        {
            for (int j = 0; j < y; j++)
            {
                if (array[i, j].Equals(myNumber))
                {
                    goto Found;
                }
            }
        }
        Console.WriteLine("The number {0} was not found.", myNumber);
        goto Finish;
    Found:
        Console.WriteLine("The number {0} is found.", myNumber);
    Finish:
        Console.WriteLine("End of search.");
    }
}

如果输入 44 ,则示例输出:

Enter the number to search for: 44

The number 44 is found.

End of search.

3.4 return 语句

return 语句终止所在方法的执行,并将程序的控制返回给调用它的方法。它还可以返回一个可选值。如果方法为 void 类型,可以省略 return 语句。

return 语句的形式如下:

return [expression];

其中:

expression 表示方法的返回值。当方法类型为 void 时不能使用 expression 参数。

示例:

在下面的示例中,方法 A() 以 double 值的形式返回变量 Area 。

using System;
class ReturnTest
{
    static double CalculateArea(int r)
    {
        double area = r * r * Math.PI;
        return area;
    }

    static void Main()
    {
        int radius = 5;
        Console.WriteLine("The area is {0:0.00}",           
          CalculateArea(radius));
    }
}

输出:

The area is 78.54

4. 检查和未检查语句

C# 语句可以在检查和非检查情况下运行。在检查情况下,算术溢出将引发异常;在非检查情况下,算术溢出将被忽略,结果将被截断。

· checked 指定检查。

· unchecked 指定非检查。

如果既未指定 checked 也未指定 unchecked ,默认取决于外部因素,比如编译器选项。

下列操作受溢出检查的影响:

· 表达式对整型使用下列预定义操作符:

++ — - (一元) + - * /

· 整型间的显式数字转换。

/checked 编译器选项使您可以为 checked 或 unchecked 关键字范围内的所有非显式整型算术语句指定检查或非检查情况。

4.1 checked 语句

checked 关键字用于对整型算术运算和转换显式启用溢出检查。

默认情况下,如果表达式产生的值超出了目标类型的范围,则常数表达式将导致编译时错误,而非常数表达式在运行时计算并将引发异常。不过,如果通过编译器选项或环境配置在全局范围内取消了溢出检查,则可以使用 checked 关键字来启用此项功能。

示例:此示例演示如何对非常数表达式使用 checked 。在运行时会报告溢出。

using System;
class OverFlowTest
{
    static short x = 32767;   // short类型的最大值
    static short y = 32767;
    // 对表达式使用 checked
    static int CheckedMethod()
    {
        int z = 0;
        try
        {
            z = checked((short)(x + y));
        }
        catch (System.OverflowException e)
        {
            Console.WriteLine(e.ToString());
        }
        return z;
    }
    static void Main()
    {
        Console.WriteLine("Checked output value is: {0}",
                     CheckedMethod());
    }
}

示例输出:

System.OverflowException: Arithmetic operation resulted in an overflow.

at OverFlowTest.CheckedMethod()

Checked output value is: 0

4.2 unchecked 语句

unchecked 关键字用于取消整型算术运算和转换的溢出检查。

在非检查情况下,如果表达式产生目标类型范围之外的值,则结果被截断。例如:

unchecked
{
    int val = 2147483647 * 2;
}

因为上面的计算在 unchecked 块中执行,所以结果对于整数来说太大这一事实被忽略,并且 val 被赋予值 -2 。默认情况下,启用溢出检测,这与使用 checked 具有相同的效果。

在上面的示例中,如果省略 unchecked ,将产生编译错误,因为表达式使用常数,结果在编译时是已知的。 unchecked 关键字还取消对非常数表达式的溢出检测,这是为了避免在运行时导致 OverflowException 。

unchecked 关键字还可以用作运算符,如下所示:

public int UncheckedAdd(int a, int b)
{
    return unchecked(a + b);
}

示例:此示例通过在常数表达式中使用 unchecked ,显示如何使用 unchecked 语句。

using System;
class TestClass
{
    const int x = 2147483647;   // Max int
    const int y = 2;
    static void Main()
    {
        int z;
        unchecked
        {
            z = x * y;
        }
        Console.WriteLine("Unchecked output value: {0}", z);
    }
}

输出:

Unchecked output value: -2

5. 非保护和固定

C# 中的语句可以在保护和非保护环境中运行,默认状态为保护环境。使用带指针的代码要求运行在非保护环境中。

关键字 unsafe :指定非保护环境。

使用了指向变量的指针,该变量就不能在内存中移动位置。这时,可使用 fixed 语句“固定”住这个变量。

关键字 fixed :防止变量重新定位。

5.1 unsafe 语句

unsafe 表示非保护环境,该上下文是任何涉及指针的操作所必需的。

unsafe 可以用作方法、属性、构造函数(不是静态构造函数)的限定符。

例如:

unsafe static void FastCopy(byte[] src, byte[] dst, int count)

{

// 非保护环境:可以使用指针。

}

非保护环境的范围包括从参数列表到方法的结尾,因此指针在以下参数列表中也可以使用:

unsafe static void FastCopy ( byte* ps, byte* pd, int count ) {...}

还可以使用不安全块从而能够使用该块内的不安全代码。例如:

unsafe

{

// 非保护环境:可以使用指针。

}

若要编译不安全代码,必须指定 /unsafe 编译器选项。无法通过公共语言运行库验证不安全代码。

示例:

// 使用 /unsafe 编译

using System;
class UnsafeTest
{
    // 非保护方法:使用 int 类型的指针。
    unsafe static void SquarePtrParam(int* p)
    {
        *p *= *p;
    }
    unsafe static void Main()
    {
        int i = 5;
        // 非保护方法:使用地址操作符(&):
        SquarePtrParam(&i);
        Console.WriteLine(i);
    }

}

输出:

25

5.2 fixed 语句

fixed 关键字防止变量被重新定位。

fixed 语句的使用格式:

fixed ( type* ptr = expr ) statement

其中:

type 表示未管辖的类型或 void 。

ptr 表示指针名称。

expr 表示隐式转换成 type* 的表达式。

statement 表示可执行的语句或语句块。

fixed 语句只能用在 unsafe 环境中执行。

fixed 语句用来设置指向变量的指针,并在 statement 执行过程中固定变量的位置。如果不使用 fixed ,指向已处理变量的指针就可能重新移动位置,该指针也就失去了作用。实际上,如果不使用 fixed 语句, C# 编译器是不允许设置指向已处理变量的指针的。

在非保护模式中,可以在堆栈上分配内存,这里的内存不受垃圾回收器的管理,因此可以不需要固定。

示例:

class FixedTest
{
    // Unsafe method: takes a pointer to an int.
    unsafe static void SquarePtrParam (int* p)
    {
        *p *= *p;
    }
    unsafe static void Main()
    {
        Point pt = new Point();
        pt.x = 5;
        pt.y = 6;
        // Pin pt in place:
        fixed (int* p = &pt.x)
        {
            SquarePtrParam (p);
        }
        // pt now unpinned
        Console.WriteLine ("{0} {1}", pt.x, pt.y);
    }
}

输出:

25 6

6. 锁定语句

lock 关键字将语句块标记为临界区,方法是获取给定对象的互斥锁,执行语句,然后释放该锁。此语句的形式如下:

Object thisLock = new Object();
lock (thisLock)
{
    // 临界区代码
}

lock 确保当一个线程位于代码的临界区时,另一个线程不进入临界区。如果其他线程试图进入锁定的代码,则它将一直等待(即被阻止),直到该对象被释放。

通常,应避免锁定 public 类型,否则实例将超出代码的控制范围。常见的结构 lock (this) 、 lock (typeof (MyType)) 和 lock ("myLock") 违反此准则:

· 如果实例可以被公共访问,将出现 lock (this) 问题。

· 如果 MyType 可以被公共访问,将出现 lock (typeof (MyType)) 问题。

· 由于进程中使用同一字符串的任何其他代码将共享同一个锁,所以出现 lock( “ myLock ” ) 问题。

最佳做法是定义 private 对象来锁定 , 或 private shared 对象变量来保护所有实例所共有的数据。

示例:下例显示的是在 C# 中使用线程的简单示例。

using System;
using System.Threading;
class ThreadTest{
   public void RunMe()
   {
         Console.WriteLine("RunMe called");
   }
    static void Main()
    {
        ThreadTest b = new ThreadTest();
        Thread t = new Thread(b.RunMe);
        t.Start();
    }
}

输出:

RunMe called

7. 异常处理语句

try - catch - finally

try 里面是执行代码 , 其中的代码 " 可能 " 产生异常 . catch 是对产生异常后的处理代码 , 可以抛出异常 , 也可以显示异常 , 也可以弹出某中提示 , 总之 catch 里是任何代码都行 , 如果你知道这钟异常产生的原因 , 可以打印此原因 , 也可以对此原因进行相应的处理 , 同时可以为多个 catch, 每个 catch( 异常类型 ) 用多个 catch 来捕获多种异常 , 也可以用所有异常的父类来捕获 ( 这样就不用写多个 catchl 了 ). 假如 try 中产生了异常 , 那么 try 从产生异常开始到 try 结束的这段代码将不会执行 , 转而去执行 catch. finally 是 try 执行完后执行 ( 没发生异常 ) 或者在 catch 后执行 ( 发生了异常 ), 也就是说 finally 无论怎么样 , 都会执行 .