Skip to content

Files

Latest commit

c70125a · Jan 8, 2022

History

History
588 lines (397 loc) · 21.9 KB

2.md

File metadata and controls

588 lines (397 loc) · 21.9 KB

二、编写表达式和语句

在第 1 章中,您看到了如何编写、编译和执行 C# 程序。示例程序在Main方法中只有一条语句。在这一章中,您将学习如何编写更多的语句并向程序中添加逻辑。为了提高效率,本书其余部分中的许多示例都是片段,但是您仍然可以将这些语句添加到Main方法中进行编译,从而更好地理解 C# 语法。也会有很多完整的程序。

写简单的陈述

通过结合语言运算符和语法,您可以用 C# 构建表达式和语句。这里有几个简单的 C# 语句的例子。

    int count = 7;
    char keyPressed = 'Q';
    string title = "Weekly Report";

代码清单 4

前面代码清单中的每个示例都有共同的语法元素:类型、变量标识符、赋值运算符、值和语句完成。类型有intcharstring,分别代表一个数字、一个字符和一个字符序列。这是 C# 提供的几个内置类型中的几个。变量是一个可以在以后的代码中使用的名称。=运算符将表达式的右侧指定给左侧。每个语句都以分号结束。

前面的例子展示了如何声明一个变量并同时执行赋值,但这不是必需的。只要你在尝试使用一个变量之前声明它,你就没事了。这里有一个单独的声明。

    string title;

代码清单 5

以及变量后来的赋值。

    title = "Weekly Report";

代码清单 6

| | 注意:C# 区分大小写,所以“title”和“Title”是两个独立的变量名。 |

C# 类型和运算符概述

C# 是一种强类型语言,这意味着编译器不会在不兼容的类型之间隐式转换。例如,您不能将string分配给int,或者将int分配给string—至少不能隐式分配。下面的代码不会编译。

    int total = "359";
    string message = 7;

代码清单 7

带双引号的“359”是字符串,不带引号的7int。虽然您不能隐式执行转换,但是有一些方法可以显式执行转换。例如,您经常会收到来自用户的文本输入,应该是int或其他类型。下面的代码清单显示了如何显式执行这些任务的几个示例。

    int total = int.Parse("359");
    string message = 7.ToString();

代码清单 8

在前面的列表中,如果字符串代表有效的int,则Parse会将字符串转换为int。对任何值调用ToString都会产生一个会编译的string

除了前面的转换示例之外,C# 还有一个强制转换运算符,允许您在允许显式转换的类型之间进行转换。假设你有一个double,它是一个 64 位的浮点类型,你想把它分配给一个int,它是一个 32 位的整数。你可以这样投:

    double preciseLength = 5.61;
    int roundedLength = (int)preciseLength;

代码清单 9

如果没有强制转换操作符,您将会收到一个编译器错误,因为double不是int。本质上,C# 编译器是在保护你不搬起石头砸自己的脚,因为给int赋值double意味着你失去了精确性。在前面的例子中,roundedLength变成了5。使用强制转换操作符可以让您告诉 C# 编译器,您知道这个操作在错误的情况下可能是危险的,但是对于您的特定情况是有意义的。

下表列出了内置类型,以便您可以查看可用的类型:

表 1:内置类型

类型(文字后缀) 描述 值/范围
字节 8 位无符号整数 0 到 255
sbyte(字节) 8 位有符号整数 -128 到 127
短的 16 位有符号整数 -32,768 至 32,767
乌肖特 16 位无符号整数 0 至 65,535
(同 Internationalorganizations)国际组织 32 位有符号整数 -2,147,483,648 至 2,147,483,647
uint 32 位无符号整数 0 至 4,294,967,295
长(l) 64 位有符号整数 –9,223,372,036,854,775,808 至 9,223,372,036,854,775,807
乌龙(乌卢) 64 位无符号整数 0 至 18,446,744,073,709,551,615
浮动(f) 32 位浮点 -3.4 × 10 38 至+3.4 × 10 38
双(d) 64 位浮点 5.0×10324至 1.7 × 10 308
十进制(m) 128 位、28 位或 29 位精度(非常适合金融应用) (-7.9 × 10 28 至 7.9×1028)/(100 至 28
弯曲件 布尔代数学体系的 对还是错
16 位 Unicode 字符(使用单引号) U+0000 至 U+FFFF
线 Unicode 字符序列(使用双引号) 例如“abc”

当意思不明确时,你应该给数字加上后缀。在以下示例中,m后缀确保了9.95文字被视为一个decimal数字:

    decimal price = 9.95m;

代码清单 10

您可以将 Unicode 值直接分配给字符。下面的示例显示了如何分配回车。

    char cr = '\u0013';

代码清单 11

您也可以使用强制转换运算符获取字符的 Unicode 值,如下所示。

    int crUnicode = (int)cr;

代码清单 12

到目前为止,您只看到了带有赋值操作符的语句,但是 C# 有许多其他操作符,允许您执行任何通用编程语言所期望的所有逻辑操作。下表列出了一些可用的运算符。

表 2: C# 运算符

种类 描述
主要的 x.y x?.y f(x) a[x] x++ x-- new typeof default checked unchecked nameof
一元的 + - ! ~ ++ x --x (T)x await x
增加的 * / %
添加剂 + -
变化 << >>
关系测试和类型测试 < > <= >= is as
平等 == !=
逻辑“与” &
逻辑异或 ^
逻辑或 &#124;
条件“与” &&
条件或 &#124;&#124;
零合并 ??
有条件的 ?:
分配 = *= /= %= += -= <<= >>= &= ^= &#124;= =>

前缀运算符在赋值前更改变量的值,后缀运算符在赋值后更改变量,如下例所示。

    int val1 = 5;
    int val2 = ++ val1;
    int val3 = 2;
    int val4 = val3--;

代码清单 13

在前面的代码清单中,val1val2都是6val3变量是1,但是val42,因为后缀运算符在赋值后求值。

三元运算符为 if-then-else 逻辑提供了简单的语法。这里有一个例子:

    decimal priceGain = 2.5m;
    string action = priceGain > 2m ? "Buy" : "Sell";

代码清单 14

?左侧是布尔表达式,priceGain > 2m。如果这是真的,在本例中,三元运算符返回第一个介于?:之间的值,即"Buy"。否则,三元运算符将在:之后返回值,即"Sell"。该语句将三元运算符"Buy"的结果赋给字符串变量action

除了内置类型之外,FCL 还有许多类型可供您日常使用。其中一个是DateTime,代表日期和时间。这里有一个快速演示,展示了一些你可以用DateTime做的事情。

    DateTime currentTime = DateTime.Now;
    string shortDateString = currentTime.ToShortDateString();
    string longDateString = currentTime.ToLongDateString();
    string defaultDateString = currentTime.ToString();
    DateTime tomorrow = currentTime.AddDays(1);

代码清单 15

前面的代码展示了如何获取当前的DateTime,日期的短表示(例如 12/8/2014),日期和时间的长表示(所有内容都拼写出来),默认的数字表示,以及如何使用DateTime方法进行计算。

| | 提示:在创建自己的类型库之前,搜索 FCL。您每天使用的许多常见类型,如日期时间,将已经存在。 |

运算符优先级和关联性

表 2 中列出的 C# 运算符按照它们的一般优先顺序列出了运算符。优先级定义了首先计算哪些运算符。优先级较高的运算符在优先级较低的运算符之前求值。

赋值和条件运算符是右关联的,所有其他运算符都是左关联的。您可以通过使用括号来更改正常的操作顺序,如下面的代码清单所示。

    int result1 = 2 + 3 * 5;
    int result2 = (2 + 3) * 5;

代码清单 16

在之前的代码中,result117,但是result225

格式化字符串

在 C# 中有不同的方法来构建和格式化字符串:串联、数字格式字符串或字符串插值。下面的代码清单演示了字符串连接。

    string name = "Joe";
    string helloViaConcatenation = "Hello, " + name + "!";
    Console.WriteLine(helloViaConcatenation);

代码清单 17

这上面印着**“你好,乔!”**到控制台。下面的例子做了同样的事情,但是使用了string.Format

    string helloViaStringFormat = string.Format("Hello, {0}!", name);
    Console.WriteLine(helloViaStringFormat);

代码清单 18

string.Format取一个格式字符串,在花括号中有数字占位符。它是从 0 开始的,所以第一个占位符是{0}。字符串后面的参数按照出现的顺序放入格式字符串中。由于name是第一个(也是唯一的)参数,string.Format{0}替换为Joe以创建一个字符串"Hello, Joe!"。为了方便控制台应用程序,WriteLine使用相同的格式。下面的代码完成了与前面代码清单中两行代码相同的任务。

    Console.WriteLine("Hello, {0}!", name);

代码清单 19

更进一步,字符串格式更强大,允许您指定列长度、对齐方式和值格式,如下面的代码所示。

    string item = "bread";
    decimal amount = 2.25m;
    Console.WriteLine("{0,-10}{1:C}", item, amount);

代码清单 20

在本例中,第一个占位符的长度为 10 个字符。默认对齐方式是右对齐,但减号会将它更改为左对齐。在第二个占位符上,C是货币格式字符串。

| | 注意:有许多字符串格式选项。可以访问https://msdn . Microsoft . com/en-us/library/dwhawy9k(v = vs . 110)。aspx 对于标准格式,https://msdn . Microsoft . com/en-us/library/0c 899 ak8(v = vs . 110)。自定义格式的 aspxhttps://msdn . Microsoft . com/en-us/library/az4se 3 k1(v = vs . 110)。日期时间格式的 aspx 。 |

C# 6 引入了一种格式化字符串的新方法,称为字符串插值。这是一种速记语法,允许您用表达式替换数字占位符,如下所示:

    Console.WriteLine($"{item}      {amount}");

代码清单 21

需要$前缀。这里,item变量的值代替{item},而amount变量的值代替{amount}。类似于数字占位符,您可以包含其他格式。

    Console.WriteLine($"{nameof(item)}: {item,-10} {nameof(amount)}: {amount:C}");

代码清单 22

nameof运算符打印出名称 【项目】 ,演示如何在占位符中使用表达式。您还可以在itemamount上看到空间和货币格式。

分支语句

您可以在代码中使用if—elseswitch语句进行分支逻辑。当您只需要为真实条件执行代码时,使用if语句,如下例所示。

    string action2 = "Sell";
    if (priceGain > 2m)
    {
        action2 = "Buy";
    }

代码清单 23

在这个例子中花括号是可选的,因为如果priceGain > 2m只有一条语句要执行。然而,它们对于多个语句是必需的。所有分支和逻辑语句都是如此。您也可以有一个else案例,如下列表所示。

    string action3 = "Do Nothing";
    if (priceGain <= 2m)
    {
        action3 = "Sell";
    }
    else
    {
        action3 = "Buy";
    }

代码清单 24

每当if语句的布尔条件为假时,就像在前面的代码示例中priceGain <= 2m一样,else子句执行。在这种情况下,action3就变成了"Buy"。当然,你可以通过增加更多的else if子句来拥有多个条件。

    string action4 = null;
    if (priceGain <= 2m)
    {
        action4 = "Sell";
    }
    else if (priceGain > 2m && priceGain <= 3m)
    {
        action4 = "Do Nothing";
    }
    else
    {
        action4 = "Sell";
    }

代码清单 25

在前面的例子中,您可以在else if子句中看到一个更复杂的布尔表达式。当priceGain2.5时,action4的值变为"Do Nothing"&&是一个逻辑运算符,如果左边和右边的表达式都为真,则成功。如果左侧或右侧的表达式为真,则逻辑||运算符成功。这些运算符还执行短路操作,如果左边的表达式导致整个表达式不为真,右边的表达式就不执行。在代码清单 25 中的else if的情况下,如果priceGain等于或小于2m,则&&运算符不会计算priceGain <= 3表达式,因为整个操作已经是假的。一旦执行了if语句的一个分支,就不会评估或执行其他分支。

请注意,我将action4设置为nullnull关键词意味着没有价值。下一章我会讲到null并说明你可以在哪里使用。

一个if语句适用于简单的分支或者复杂的条件,比如前面的else if子句。但是,当您有多个案例并且所有表达式都是常量值时,例如intstring,您可能更喜欢switch语句。以下示例使用switch语句根据天气预报选择合适的设备。

    string currentWeather = "rain";
    string equipment = null;
    switch (currentWeather)
    {
        case "sunny":
            equipment = "sunglasses";
            break;
        case "rain":
            equipment = "umbrella";
            break;
        case "cold":
        default:
            equipment = "jacket";
            break;
    }

代码清单 26

switch语句试图用它的一个case语句来匹配一个值,在这个例子中是currentWeather。它使用default不匹配的情况。所有case语句必须以break语句结束。唯一允许的时间是当case没有身体时,如"cold" casedefault所示,它们都将equipment设置为"jacket"。由于currentWeather"rain"equipment变为"umbrella",其他情况不执行。

除了分支语句之外,您还需要多次执行一组操作的能力,这就是 C# 循环的由来。在讨论循环之前,让我们看看数组和集合,它们保存着循环可以使用的数据。

数组和集合

有时,您需要将集合中的许多项组合在一起,以便在内存中管理它们。为此,可以使用数组或。NET 框架。下面的示例演示如何创建数组。

    int[] oddNumbers = { 1, 3, 5 };
    int firstOdd = oddNumbers[0];
    int lastOdd = oddNumbers[2];

代码清单 27

在这里,我已经用三个值声明并初始化了数组。数组和集合是从 0 开始的,所以firstOdd1lastOdd5[x]语法,其中x是一个数字,被称为索引器,因为它允许您在索引指定的位置访问数组。这里还有一个用string代替int的例子。

    string[] names = new string[3];
    names[1] = "Joe";

代码清单 28

在这个例子中,我实例化了一个数组来保存三个字符串。默认情况下,所有字符串都等于null。该代码将第二个字符串设置为"Joe"

除了数组之外,您还可以使用所有类型的数据结构,例如ListStackQueue等等,它们都是 FCL 的一部分。下面的例子展示了如何使用List。记得为System.Collections.Generic添加一个using子句来使用List<T>类型。

    List<decimal> stockPrices = new List<decimal>();
    stockPrices.Add(56.23m);
    stockPrices.Add(72.80m);
    decimal secondStockPrice = stockPrices[1];

代码清单 29

在这个示例中,我实例化了一个新的List集合。<decimal>是一个泛型类型,表示这是一个强类型列表,只能保存类型为decimal的值;这是decimalList。那份清单有两项。请注意我是如何使用类似数组的索引器语法来读取(从 0 开始)stockPrices列表中的第二项的。

循环语句

C# 支持多个循环,包括forforeachwhiledo。下面的代码清单执行类似的逻辑。

    double[] temperatures = { 72.3, 73.8, 75.1, 74.9 };
    for (int i = 0; i < temperatures.Length; i++)
    {
        Console.WriteLine(i);
    }

代码清单 30

for循环将i初始化为0,确定i小于temperature数组中的项目数,执行Console.WriteLine,然后递增i。它继续执行,直到条件(i < temperatures.Length)为假,然后继续执行程序中的下一条语句。

    foreach (int temperature in temperatures)
    {
        Console.WriteLine(temperature);
    }

代码清单 31

代码清单 31 中使用的foreach循环更简单,将对temperatures数组中的每个值执行。

接下来是一个while循环的例子。

    int tempCount = 0;
    while (tempCount < temperatures.Length)
    {
        Console.WriteLine(tempCount);
        tempCount++ ;
    }

代码清单 32

while循环评估条件,如果条件为真,则执行。请注意,我在每次迭代中将tempCount初始化为0,并在循环内部增加tempCount

最后,下面的例子展示了如何编写一个do - while循环。

    int tempCount2 = 0;
    do
    {
        Console.WriteLine(tempCount2++);
    }
    while (tempCount2 <= temperatures.Length);

代码清单 33

一个do - while循环适合你至少要执行一次逻辑的时候。本示例将tempCount2作为参数增加到Console.WriteLine。请记住,后缀运算符会在求值后更改变量。

收尾

这是一个计算器程序,汇集了本章的一些概念,加上一些额外的功能。你可以把它输入到你的编辑器中,然后执行它来练习。

    using System;
    using System.Text;

    /*
        Title: Calculator
        By: Joe Mayo
    */

    class Calculator
    {
        /// <summary>
        /// This is the entry point.
        /// </summary>
        static void Main()
        {
            char firstChar = 'Q';
            bool keepRunning = true;

            do
            {
                Console.WriteLine();
                Console.Write("What do you want to do? (Add, Subtract, Multiply, Divide, Quit): ");
                string input = Console.ReadLine();
                firstChar = input[0];

                // This is used in both the if statement and the do-while loop.
                keepRunning = !(firstChar == 'q' || firstChar == 'Q');

                double firstNumber = 0;
                double secondNumber = 0;

                if (keepRunning)
                {
                    Console.Write("First Number: ");
                    string firstNumberInput = Console.ReadLine();
                    firstNumber = double.Parse(firstNumberInput);

                    Console.Write("Second Number: ");
                    string secondNumberInput = Console.ReadLine();
                    secondNumber = double.Parse(secondNumberInput);
                }

                double result = 0;
                switch (firstChar)
                {
                    case 'a':
                    case 'A':
                        result = firstNumber + secondNumber;
                        break;
                    case 's':
                    case 'S':
                        result = firstNumber - +secondNumber;
                        break;
                    case 'm':
                    case 'M':
                        result = firstNumber * secondNumber;
                        break;
                    case 'd':
                    case 'D':
                        result = firstNumber / secondNumber;
                        break;
                    default:
                        result = -1;
                        break;
                }

                Console.WriteLine();
                Console.WriteLine("Your result is " + result);

            } while (keepRunning);
        }
    }

代码清单 34

上一个程序演示了一个do - while循环、if语句、switch语句以及与用户的基本控制台通信。

这里有几个你还没见过的字符串特性。第一种是程序使用Console.ReadLine从用户那里读取输入字符串的输入文本。请注意从字符串中读取第一个字符的索引器语法。您可以通过这种方式读取字符串的任何字符。另外,看看程序底部打印"Your result is " + result的地方,它将一个字符串和数字连接起来。使用+运算符进行连接是构建字符串的简单方法。另一种构建字符串的方法是使用名为StringBuilder的类型,您可以这样使用:

    StringBuilder sb = new StringBuilder();
    sb.Append("Your result is ");
    sb.Append(result.ToString());
    Console.WriteLine(sb.ToString());

代码清单 35

您还需要在文件顶部添加一个using System.Text;子句。在同一个字符串上使用了大约四次 concatenate 运算符+之后,您可以考虑改为使用StringBuilder重写。string类型是不可变的,意味着不能修改。这也意味着每个串联操作都会导致 CLR 在内存中创建一个新的字符串。

计算器程序也有不编译的多行和单行注释,但是可以帮助您根据需要记录代码。以下是多行注释:

    /*
        Title: Calculator
        By: Joe Mayo
    */

代码清单 36

以下是单行注释:

    // This is used in both the if statement and the do-while loop.

代码清单 37

单行注释的扩展是使用三个斜线和一组 XML 标记的约定,称为文档注释。

    /// <summary>
    /// This is the entry point.
    /// </summary>

代码清单 38

总结

C# 有一整套运算符和类型,允许您编写各种表达式和语句。使用分支语句和循环,您可以编写自己选择的逻辑。本章中的所有代码都在Main方法中,但显然这是不够的,你会很快从中受益。下一章将探索一些新的 C# 特性,以帮助用方法和属性组织代码。