zoukankan      html  css  js  c++  java
  • Essential.C#第三章 操作符和控制流

    达式内的操作数产生合适动作,Operators provide syntax for performing different calculations or actions appropriate for the operands within the calculation。控制流语句提供了程序内的条件逻辑和多次循环一段代码。稍后我会引入IF控制流语句。本章着眼于布尔表达式这个概念,它内嵌在许多控制流语句内,并指出不能将整数型转换成布尔型的优势。本章的末尾还会讨论预处理,以及它的指令。

    image

    操作符

       你已经学习了预定义数据类型,现在你该开始学习如何使用操作符将这些类型组合起来进行运算。

    操作符

        操作符指示了表达式内的操作。比如,一个数学表达式,操作一组值(这就叫做操作数)得到新值。比如,清单3.1就有两个操作数,数字4和2。它们使用减号操作符,- ,组合在一起。然后将结果赋值给变量total

    清单3.1

    total = 4 - 2;

        操作符通常被分成三类:一元,二元,三目运算符,相对于的操作数的数目就是1,2,3。本章重点是一元,和二元运算符,三目运算符在本章后面会有讲解。

    加减单目运算符(+,-)

        有时你想改变数字变量的正负号。这时,单目减号运算符就会派上用场,负号操作符相当于将一个数字乘以-1。加号单目运算符不影响值,对于C#来说有点多余,不过是为了对称。

    二元算术操作符(+,-,*,/,%)

        二元操作符需要两个操作数,分别把操作数置于操作符的两端。二元操作符需要避免丢失结果精度。在清单3.3中就是一个二元操作符,更具体的这是一个算术二元操作符。操作数存在于算术操作符的两端,并将它们的结果赋值。其他算术操作符是加,除,乘,余(有时也叫模)

    清单3.3

    class Program
    {
        static void Main(string[] args)
        {
            int numerator;
            int denominator;
            int quotient;
            int remainder;
            System.Console.Write("Enter the numerator: ");
            numerator = int.Parse(System.Console.ReadLine());
            System.Console.Write("Enter the denominator: ");
            denominator = int.Parse(System.Console.ReadLine());
            System.Console.WriteLine(
                "{0} / {1} = {2} with remainder {3}",
                numerator, denominator, quotient, remainder);
        }
    }

    输出3.3

    Enter the numerator: 23
    Enter the denominator: 3
    23 / 3 = 7 with remainder 2

    注意使用二元运算符的顺序关联性。二元运算符的顺序是从左至右。而赋值操作符的顺序是从右至左。 On its own, however, associativity does not specify whether the division will occur before or after the assignment。操作符的优先级按如下顺序:

    1)*,/,%  2)+,-  3) =

    所以,你可以假想除法和求余操作符在赋值之前会发生什么。

        如果你忘记为这些二元操作符结果赋值,你将会收到像输出3.2那样的编译器错误。

    输出3.2

    ... error CS0201: Only assignment, call, increment, decrement,
    and new object expressions can be used as a statement

    优先级和顺序关联性

        在程序语言中也有数学上的顺序关联性概念。顺序关联性是指操作数如何分类,就是确定操作符顺序。对于在一个表达式中多次出现单一操作符,操作符的顺序关联性将不会影响结果。二元操作符比如+和- 具有顺序关联性因为操作符出现的顺序并不重要,a+b+c的结果于先执行a+b还是执行b+c都是一样的。

        当在一个语句内出现了不同的操作符,就需要规定操作符的执行顺序了,这就叫优先级。比如,规定乘法操作符先于加法操作符执行。a+b*c

    在字符串中使用加法操作符

        在非数字类型中也能使用操作符,比如可以使用加法操作符连接两个以上的字符串。

    清单3.4

    class Program
    {
        static void Main(string[] args)
        {
            short windSpeed = 42;
            System.Console.WriteLine(
                "The original Tacoma Bridge in Washington\nwas "
                + "brought down by a "
                + windSpeed + " mile/hour wind.");
        }
    }

    输出3.3

    The original Tacoma Bridge in Washington
    was brought down by a 42 mile/hour wind.

    由于在不同文化中句子的结构式部一样的。所以在需要本地化时,程序员应该尽量避免使用加法操作符连接字符串。应该首选混合格式(去看第一章)

    在算术运算中使用字符

        上一章介绍了Char类型,尽管char类型中存储的是字符而不是数字,但它依然是一个整数型。它可以和别的整型一起参与算术运算。所以,char类型值不会被解释为字符,而是它的基本值。比如,对于字符3,它的Unicode值是0x33,十进制值51.数字4,Unicode值是0x34,十进制值52。3加4的十六进制结果是0x167,而十进制结果是103.这个值和字母g的值相同。

    清单3.5

    int n = '3' + '4';
    char c = (char)n;
    System.Console.WriteLine(c);  // Writes out g.

        利用这种特性,你可以快速的将两个字符变成另一个。比如,字母f和字母c的距离是3.你可以将两者相减得到这个值。

    清单3.6

    int distance = 'f'-'c';
    System.Console.WriteLine(distance);//output 3

    浮点的特性

        浮点类型,float和double,有一些特别的性质。比如它们处理精度的方式。本节会看一些特别的例子,还会展示浮点型的一些特有的性质。

        float,有7位有效精度。可以容纳的值是1234567和0.1234567。如果你将两个float相加,其结果将被四舍五入为1234567,因为float数字的小数部分只能容纳7位数字。这个类型的约数是很重要的,尤其是在多次计算或等比时。

        注意,对于简单的赋值操作也会有误差,比如double number = 4.2F。由于double能比float更精确,C#编译器将将根据情况处理这个表达式double number=4.1999998092651367。float会把4.1999998092651367看作4.2,而double绝对不会将其以4.2呈现在大家面前。

    浮点类型不等式的非确定性

        当比较相等值时,对于float型,它的误差是很让人头痛的。它们居然不相等!

    清单3.7

    class Program
    {
        static void Main(string[] args)
        {
            decimal decimalNumber = 4.2M;
            double doubleNumber1 = 0.1F * 42F;
            double doubleNumber2 = 0.1D * 42D;
            float floatNumber = 0.1F * 42F;
            System.Diagnostics.Trace.Assert(decimalNumber != (decimal)doubleNumber1);
            // Displays: 4.2 != 4.20000006258488 
            System.Console.WriteLine(
                "{0} != {1}", decimalNumber, (decimal)doubleNumber1);
            System.Diagnostics.Trace.Assert((double)decimalNumber != doubleNumber1);
            // Displays: 4.2 != 4.20000006258488 
            System.Console.WriteLine(
                "{0} != {1}", (double)decimalNumber, doubleNumber1);
            System.Diagnostics.Trace.Assert((float)decimalNumber != floatNumber);
            // Displays: (float)4.2M != 4.2F
            System.Console.WriteLine(
                "(float){0}M != {1}F",
                (float)decimalNumber, floatNumber);
            System.Diagnostics.Trace.Assert(doubleNumber1 != (double)floatNumber);
            // Displays: 4.20000006258488 != 4.20000028610229 
            System.Console.WriteLine(
                "{0} != {1}", doubleNumber1, (double)floatNumber);
            System.Diagnostics.Trace.Assert(doubleNumber1 != doubleNumber2);
            // Displays: 4.20000006258488 != 4.2
            System.Console.WriteLine(
                "{0} != {1}", doubleNumber1, doubleNumber2);
            System.Diagnostics.Trace.Assert(floatNumber != doubleNumber2);
            // Displays: 4.2F != 4.2D
            System.Console.WriteLine(
                "{0}F != {1}D", floatNumber, doubleNumber2);
            System.Diagnostics.Trace.Assert((double)4.2F != 4.2D);
            // Display: 4.19999980926514 != 4.2
            System.Console.WriteLine(
                "{0} != {1}", (double)4.2F, 4.2D);
            System.Diagnostics.Trace.Assert(4.2F != 4.2D);
            // Display: 4.2F != 4.2D
            System.Console.WriteLine(
                "{0}F != {1}D", 4.2F, 4.2D);
        }
    }

    输出3.6

    4.2 != 4.20000006258488
    4.2 != 4.20000006258488
    (float)4.2M != 4.2F
    4.20000006258488 != 4.20000028610229
    4.20000006258488 != 4.2
    4.2F != 4.2D
    4.19999980926514 != 4.2
    4.2F != 4.2D

        Assert()方法用来当其参数表达式为false时,会弹出一个对话框。然而在本代码中的所有条件都是true。尽管代码中的这些值表面上是相等的,事实上,由于float的误差它们不相等。不过,这不会产生四舍五入错误。C#编译器会执行这些算式。即使,你不通过计算,而是直接用4.2F赋值,比对依然是不相等的。

        为了避免由于浮点类型误差原因而产生的未预料结果,开发者应该避免使用这些类型做条件判断。否则条件判断就要包含误差。有一种最简单的实现方式,用一个值减去另一个值,然后比较这个结果是否小于最大误差。最好的方法是用decimal类型代替float类型。

        你应该知道浮点型的一些特质。比如,你想用一个整数除以零,对于精确性的数据类型比如int和decimal,就会产生错误。而对于float和double类型,会产生一个特殊的值。就像清单3.8那样

    清单3.8

    float n = 0f;
    // Displays: NaN 
    System.Console.WriteLine(n / 0);

    输出3.7

    NaN //对于中文系统会产生“非数字”

        在数学上,有些操作符没有定义。在C#中,0除任何数都显示NaN。同理负数的平方根也是NaN(System.Math.Sqrt(-1))

        浮点数字可能会边界溢出。比如,float类型的上边界是3.4E38。当数字溢出这个边界,会输出Infinity。同样,float的下界是-3.4E38.当分配的值低于此值,就会显示-Infinity。

    清单3.9

    // Displays: -Infinity 
    System.Console.WriteLine(-1f / 0);
    // Displays: Infinity 
    System.Console.WriteLine(3.402823E+38f * 2f);

        浮点数字只能是非常非常接近零值,但不能是零。如果值低于float或double类型的下界,那么数值将显示负零或正零,这依赖数字是正数还是负数,它会显示成-0或0

    括号操作符

        括号操作符允许你将操作数和操作数分组,以便一起求他们的值。它具有最高的优先顺序。比如,下面两个算式的值完全不同的。

        (60/10)*2
        60/(10*2)

    第一个算式等于12,第二个表达式结果是3。因此是括号影响了表达式的最终结果。

        有时括号操作符并能改变结果,因为它没有改变优先顺序。不过,使用括号可以提高代码可读性。

    赋值操作符(+=,-=,*=,/=,%=)

        在第一张讨论了一个简单的赋值操作符,它是将右侧的值放入左侧的变量中。Other assignment operators combine common binary operator calculations with the assignment operator。

    清单3.10

    int x;
    x = x + 2;

         首先计算X+2,然后将计算结果返回给x。由于这种类型的操作比较频繁,就产生了把计算和赋值放在一起的一个操作符。+=操作符表示左侧的变量加右侧的值。

    清单3.1

    int x;
    x += 2;

    此代码和3.09的是一个样的。

        还有许多类似功能的组合赋值操作符。加,减,乘,除都有。

    递增和递减操作符

        C#还包含递增或递减一个数字的操作符。递增操作符++,是每次调用都增加。清单3.13中的所有语句都表达一个意思。

    清单3.13

    spaceCount = spaceCount + 1;
    spaceCount += 1;
    spaceCount++;

        同样,你也可以使用递减操作符--,来处理变量

    清单3.14

    lines = lines - 1;
    lines -= 1;
    lines--;

    在循环中递减

        递增和递减操作符常常用在循环语句中,比如while循环。比如,清单3.15用递增操作符来迭代字母表中的每个字符。

    清单3.15

    class Program
    {
        static void Main(string[] args)
        {
            char current;
            int asciiValue;
            // Set the initial value of current.
            current = 'z';
            do
            {
                // Retrieve the ASCII value of current.
                asciiValue = current;
                System.Console.Write("{0}={1}\t", current, asciiValue);
                // Proceed to the previous letter in the alphabet;
                current--;
            }
            while (current >= 'a');
        }
    }

    输出3.9

    z=122   y=121   x=120   w=119   v=118   u=117   t=116   s=115   r=114 
    q=113   p=112   o=111   n=110   m=109   l=108   k=107   j=106   i=105 
    h=104   g=103   f=102   e=101   d=100   c=99    b=98    a=97

    递增和递减操作符常用来计数指定的操作执行的多少次。注意,在本例中,递增操作符使用的是字符类型。

        作为一个赋值操作符,递增操作符也可以返回一个值,换句话说,在使用递增或递减操作符的同时也能使用赋值操作符。

    清单3.16

    static void Main(string[] args)
    {
        int count;
        int result;
        count = 0;
        result = count++;
        System.Console.WriteLine("result = {0} and count = {1}",
                                result, count);
    }

    输出3.10

    result = 0 and count = 1

        你也许很惊讶,count在递增以前被赋值给了result。最终结果就是result的值是0,而count的值是1.

        如果你想让递增或递减操作符优先于赋值操作符。你需要将递增操作符置到变量前面。

    清单3.17

    static void Main(string[] args)
    {
        int count;
        int result;
        count = 0;
        result = ++count;
        System.Console.WriteLine("result = {0} and count = {1}",
                                result, count);
    }

    输出3.11

    result = 1 and count = 1

        递增或递减操作符在表达式中的位置,将直接影响代码功能。如果递增或递减操作符出现在操作数之前,将会返回一个新的值。比如,x是1,而++x会返回2,而如果使用操作符后置,即x++,它的返回值依然会是1。不管操作符是前置还是后置,x的最后结果都会改变。

    清单3.18

    class Program
    {
        static void Main(string[] args)
        {
            int x;
            x = 1;
            // Display 1, 2.
            System.Console.WriteLine("{0}, {1}, {2}", x++, x++, x);
            // x now contains the value 3.
            // Display 4, 5.
            System.Console.WriteLine("{0}, {1}, {2}", ++x, ++x, x);
            // x now contains the value 5.
            // ...
        }
    }

    输出3.12

    1, 2, 3
    4, 5, 5

        根据清单3.18的演示,递增或递减操作符在操作数的出现位置,能影响最终结果。前置递增/递减操作符的结果是计算操作数之后的值。后置递增/递减操作符的结果是操作数改变之前的值。开发者应该十分小心使用这些操作符。当对将要发生什么有怀疑时,请独立使用这些操作符,将他们放在单独的语句中。这种方式能提高代码可读性,并不会产生歧义。

    递增和递减的线程安全

        尽管递增和递减操作符十分简洁了,但这些操作符并没有原子性。在线程上下文中可能会影响这个操作符的执行,并产生竞态问题。使用lock语句来避免竞态问题。System.Threading.Interlocked中包含线程安全的递增和递减方法Increment(),Decrement().These methods rely on processor functions for performing fast thread-safe increments and decrements.

    常量表达式

        上一章讨论了常量。使用操作符就可以将多个常量组合成常量表达式。默认时,常量表达式是在C#编译器编译时运行一次(而不是程序每次调用时执行)。比如,可以将一天内的秒数作为一个常量表达式,它的结果可以用在别的表达式中。

        清单3.19中的关键字const,在编译时就锁定了结果,在代码中任何修改结果的尝试都会产生编译器错误。

    清单3.19
    image

    注意,secondsPerWeek也是一个常量表达式。由于表达式中的操作数也是常量,所以编译能输出结果。

    流控制

        本节讨论如何用条件改变语句的执行顺序。然后,你将学习如何通过循环结构多次执行一组语句。

    语句 常用语法规则 实例
    if if(布尔表达式)
      内嵌语句
    if (input == “quite”)
    {
      System.Console.WriteLine(“Game end”);
      return;
    }
    while while(布尔表达式)
      内嵌语句
    while(count < total)
    {
      System.Console.WriteLine(“count ={0}”,count);
      count++;
    }
    do…while do
      内嵌语句
    while(布尔表达式)
    do
    {
      System.Console.WriteLine(“Enter name:” );
      input = System.Console.ReadLine();
    }
    while(input != “exit”);
    for for(for初始值;
      布尔表达式;
      for迭代器)
      内嵌语句
    for(int count = 1; count <= 10; count++)
    {
       System.Console.WriteLine(“count ={0}”,count);
    }
    foreach

    continue
    foreacth(类型标示符 in 表达式)
      内嵌语句

    continue;
    foreach(char Letter in email)
    {
      if(!insideDomain)
      {
         if(Letter == ’@’)
            insideDomain = true;
         continue;
      }
      System.Console.Write(Letter);
    }
    switch

    break

    goto
    switch(控制类型表达式)
    {
      …
      case 常量表达式:
         语句列
         跳转语句
      default:
         语句列
    }

    switch(input)
    {
        case "exit":
        case "quit":
            System.Console.WriteLine(
              "Exiting app....");
            break;
        case "restart":
            Reset();
            goto case "start";
        case "start":
             GetMove();
             break;
        default:
            System.Console.WriteLine(
              input);
            break;
    }..

    上表列出了主要的流程控制语句。注意常用语法规则列只是常用的语句,而不是完整的语句汇总。内嵌语句是指任何语句,但不包括声明语句或标签。

        附录B的程序中可以找到表3.1的所有控制语句。

        本章剩余部分会对每个语句的细节做描述。接下来是if语句,它需要介绍,包含代码块,范围,布尔表达式。并且在继续介绍其他控制流语句前还要引入位操作符。读者会发现本表很熟悉,因为C#和其他语言和相似,你就可以跳转到C#预处理指令那一节,或直接跳到本章末尾。

    老实说,这一章翻译起来太无聊了,就是if…else…,不搞这一章了。回头再说。不过到目前为止我已经培养出了对英语的兴趣,不再惧怕这东西。词汇量还是个问题。暂时不去碰语法。

  • 相关阅读:
    PHP xml_parser_set_option() 函数
    PHP xml_parser_get_option() 函数
    PHP xml_parser_free() 函数
    PHP xml_parser_create() 函数
    显示模式 | @media.display-mode (Media Queries)
    显示 | display (Flexible Box Layout)
    时间 | <time> (Values & Units)
    方向 | direction (Writing Modes)
    方向 | @viewport.orientation (Device Adaptation)
    文本阴影 | text-shadow (Text Decoration)
  • 原文地址:https://www.cnblogs.com/yaoshi641/p/1546148.html
Copyright © 2011-2022 走看看