表达式的定义

013,014,015,016 表达式、语句详解 - 图1

什么是表达式:An expressions is a syntactic entity whose evaluation either produces a value or fails to terminate, in which case the expression is undefined.

evaluate a single value, object, method, or namespace

Single Value

  1. int x = 100;
  2. x++;
  3. ++x;

Object

  1. (new Form()).ShowDialog();

Method

  1. // Console.WriteLine 就是方法
  2. Action myActino = new Action(Console.WriteLine);

Namespace

  1. // System.Windows.Forms 名称空间访问表达式
  2. System.Windows.Forms.Form myForm = new Form();

各类表达式概览

013,014,015,016 表达式、语句详解 - 图2

C# 语言中表达式的分类

  • A value. Every value has an associated type.
    回顾上一节讲的操作符,并讲解由这些操作符组成的表达式所返回的类型。 013,014,015,016 表达式、语句详解 - 图3
  • 条件操作符?:总会返回精度高的那个类型:

    1. var x = 5>3?2:3.0;
    2. Console.WriteLine(x.GetType().FullName);
    3. // System.Double
  • A variable. Every variable has an associated type.

    1. int x = 100;
    2. int y;
    3. y = x;
  • A namespace.

    1. System.Windows.Forms.Form myForm = new Form();
  • A type.

    1. var t = typeof(Int32);
  • A method group
    Console.WriteLine,这是一组方法(19 种重载),重载决策决定具体调用那种方法。

  • A null literal.
    null 值也是一个值。

    1. Form myForm = null;
  • A property access.

  • An event access. ```csharp static void Main(string[] args) { var myForm = new Form(); // 访问属性 myForm.Text = “Hello”; // 访问事件 myForm.Load += MyForm_Load; myForm.ShowDialog(); }

private static void MyForm_Load(object sender, EventArgs e) { var form = sender as Form; if (form == null) { return; }

  1. form.Text = "New Title";

}

  1. - An indexer access.
  2. ```csharp
  3. var intList = new List<int>() {1, 2, 3 };
  4. var x = intList[2];
  • Nothing.对返回值为 void 方法的调用013,014,015,016 表达式、语句详解 - 图4

复合表达式的求值

C#语言定义文档里面未明确定义复合表达式,但确实常用。
注意操作符的优先级和同优先级操作符的运算方向(除了赋值操作符,基本都是从左向右)。

参考C#语言定义文档

第七章专门讲表达式。
对于初学者,仅作参考,不必深究 —— 毕竟我们是在学习语言、不是去实现这门语言。

语句的定义

广义:
013,014,015,016 表达式、语句详解 - 图5

狭义:
013,014,015,016 表达式、语句详解 - 图6

实例讲解高级语言与低级语言的差异

C 版本

013,014,015,016 表达式、语句详解 - 图7

013,014,015,016 表达式、语句详解 - 图8

013,014,015,016 表达式、语句详解 - 图9

C# 版本

C# 看汇编输出

  1. 找到编译的 Application013,014,015,016 表达式、语句详解 - 图10
  2. 在电脑里面搜索找到 Developer Command Prompt013,014,015,016 表达式、语句详解 - 图11
  3. 输入 ildasm 命令013,014,015,016 表达式、语句详解 - 图12
  4. 用 ildasm 工具打开第 1 步找到的 Application013,014,015,016 表达式、语句详解 - 图13
  5. 双击具体方法,查看编译结果013,014,015,016 表达式、语句详解 - 图14

PS:推荐使用 JetBrains 家的 dotPeek 进行反编译。

实例演示控制流(flow of control)

  1. static void Main(string[] args)
  2. {
  3. string input = Console.ReadLine();
  4. try
  5. {
  6. double score = double.Parse(input);
  7. if (score >= 60)
  8. {
  9. Console.WriteLine("Pass!");
  10. }
  11. else
  12. {
  13. Console.WriteLine("Failed!");
  14. }
  15. }
  16. catch
  17. {
  18. Console.WriteLine("Not a number!");
  19. }
  20. }

013,014,015,016 表达式、语句详解 - 图15

程序没变,控制流变了。
013,014,015,016 表达式、语句详解 - 图16

语句详解

013,014,015,016 表达式、语句详解 - 图17

嵌入式语句
013,014,015,016 表达式、语句详解 - 图18

声明语句

讲解了局部变量声明与局部常量声明,详情参见 C#语言定义文档。
013,014,015,016 表达式、语句详解 - 图19

表达式语句

013,014,015,016 表达式、语句详解 - 图20

expression-statement 用于计算所给定的表达式。由此表达式计算出来的值(如果有)被丢弃。

  1. static void Main(string[] args)
  2. {
  3. // Add 产生的 7.0,如果前面没有拿变量接收它,值被丢弃了。
  4. Add(3.0, 4.0);
  5. }
  6. static double Add(double a,double b)
  7. {
  8. return a + b;
  9. }

Single Responsibility 单一职责原则:一个方法尽量只做一件事情。

下面 x+y 这种语句在 C 语言里面是允许的,在 C# 里面不允许。

  1. int x = 100;
  2. int y = 200;
  3. x+y;

块语句

013,014,015,016 表达式、语句详解 - 图21

  • 块语句无论什么时候都被编译器当做一条语句来看待
  • 编译器认为块语句是一条完整的语句(即块语句最后不用加;号)

Code Snippet
Ctrl + }:跳转至该花括号对应的花括号处。

变量的作用域:块之内声明的变量,作用域仅在块内。

  1. static void Main(string[] args)
  2. {
  3. int x = 100;
  4. {
  5. Console.WriteLine(x);
  6. int y = 200;
  7. Console.WriteLine(y);
  8. }
  9. // Error CS0103 当前上下文中不存在名称“y”
  10. Console.WriteLine(y);
  11. }

选择(判断、分支)语句

013,014,015,016 表达式、语句详解 - 图22

if 语句

013,014,015,016 表达式、语句详解 - 图23

编程规范推荐即使只有一条语句,也建议使用块语句。

Code Snippet 013,014,015,016 表达式、语句详解 - 图24

程序员经常修缮前人代码以满足新需求,但因为需求变化(需要实现新逻辑) + 业务逻辑复杂(不敢修改旧代码),导致写出来的代码很臃肿,还很容易陷入思维定式。

无论多长的 if else,它都是一条语句。之所以能有 else if{} 这种结构,也是因为 if{} 是一条语句。

switch 语句

image.png
注:从 C# 7.0 开始 switch 表达式已支持任何非 null 表达式

  1. int score = 101;
  2. switch (score/10)
  3. {
  4. case 10:
  5. if (score==100)
  6. {
  7. goto case 8;
  8. }
  9. else
  10. {
  11. goto default;
  12. }
  13. // 只有单独的标签才能连起来写。
  14. case 8:
  15. case 9:
  16. // 一旦有了具体的 section,就必需配套 break。
  17. Console.WriteLine("A");
  18. break;
  19. ...
  20. default:
  21. break;
  22. }

Code Snippet

  1. Ctrl + L 剪切一整行
  2. 013,014,015,016 表达式、语句详解 - 图26

try 语句

013,014,015,016 表达式、语句详解 - 图27

可以通过 MSDN 查方法相应的异常。
如 Int32.Parse 方法 (String) 就有以下异常。
013,014,015,016 表达式、语句详解 - 图28

  • 应该把释放系统资源的语句写在 finally block 里面
  • 有时候也在 finally block 里面写 log

throw 将异常抛给调用者。
throw 关键字的语法比较灵活。

  1. try
  2. {
  3. ...
  4. }
  5. catch(OverflowException)
  6. {
  7. throw;
  8. }

迭代(循环)语句

  • while 语句按不同条件执行一个嵌入语句零次或多次
  • do 语句按不同条件执行一个嵌入语句一次或多次
  • for 语句计算一个初始化表达式序列,然后,当某个条件为真时,重复执行相关的嵌入语句并计算一个迭代表达式序列
  • foreach 语句用于枚举一个集合的元素,并对该集合中的每个元素执行一次相关的嵌入语句

for 循环圆括号里面的的三部分都是 opt 可选的(两个分号不能省略),由此可以组成许多平时用不到的奇葩结构。

遍历和迭代器

Array 实现了 IEnumerable
013,014,015,016 表达式、语句详解 - 图29

013,014,015,016 表达式、语句详解 - 图30

013,014,015,016 表达式、语句详解 - 图31

集合遍历的底层原理和迭代器,foreach 语句就是对集合遍历的简记法。

  1. static void Main(string[] args)
  2. {
  3. var intArray = new int[] { 1, 3, 5 ,7};
  4. IEnumerator enumerator = intArray.GetEnumerator();
  5. while (enumerator.MoveNext())
  6. {
  7. Console.WriteLine(enumerator.Current);
  8. }
  9. var intList = new List<int>() { 2, 4, 6, 8 };
  10. IEnumerator enumerator2 = intList.GetEnumerator();
  11. while (enumerator2.MoveNext())
  12. {
  13. Console.WriteLine(enumerator2.Current);
  14. }
  15. }

跳转语句

  • continue 语句将开始直接封闭它的 while、do、for 或 foreach 语句的一次新迭代
  • break 语句将退出直接封闭它的 switch、while、do、for 或 foreach 语句
  • goto 语句将控制转到由标签标记的语句
    • goto 语句基本被淘汰
  • throw 语句将引发一个异常
    • throw 语句语法比较灵活,它后面可以什么都不跟
  • return 语句会将控制返回到出现 return 语句的函数的当前调用方
    • 提前 return 原则
    • 方法的每个分支里面都需要有 return

提前 return 原则

通过提前 return 可以让代码阅读者立刻就鉴别出来程序将在什么情况下 return,同时减少 if else 嵌套,写出更优雅的代码。

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. Greeting("Mr.Duan");
  6. }
  7. static void Greeting(string name)
  8. {
  9. if (string.IsNullOrEmpty(name))
  10. {
  11. // 通过尽早 return 可以让代码阅读者立刻就鉴别出来
  12. // name 参数在什么情况下是有问题的
  13. return;
  14. }
  15. Console.WriteLine("Hello, {0}", name);
  16. }
  17. }

参考

Docs Statements