七、表达式、语句详解

七、表达式、语句详解

表达式的定义

在这里插入图片描述

C#中的委托实际上是模仿c/c++中的函数指针,Java没有这个语法

namespace ExpressionSimple {
    internal class Program {
        static void Main(string[] args) {
            Action myAction = new Action(Console.WriteLine);
            /*Console.WriteLine
             * 就是一个表达式,
             * 返回一个方法
             */
        }
    }
}

各类表达式概览

在这里插入图片描述

使用某个操作符所组成的表达式的返回类型,都是不确定的,要看具体情况。

    var x = 3 < 5;
    Console.WriteLine(x);
    Console.WriteLine(x.GetType().Name);
    //bool表达式可以产生一个bool类型的值
}

某些操作符,会使得数据类型提升,此时表达式所返回的值,就是提升后的数据类型

as表达式

如果as成功了,as表达式的值,则和as右边的数据类型一致,如果不成功,就返回null

在这里插入图片描述

赋值表达式的值,就是等号左边的变量

int x = 5;
int y;
Console.WriteLine(y = x);
//这里打印出来是5
Console.WriteLine((y = x).GetType().FullName);

语句定义

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在这里插入图片描述

namespace StatementsExample {
    internal class Program {
        static void Main(string[] args) {
            string input = Console.ReadLine();
            /*程序写好之后语句虽然不会再改变
             * 但是因为输入的不一样,控制流是会发生改变的
             * 
             */
            try {
                double score = double.Parse(input);
                //将输入的字符串转化成double

                if(score >= 60) {
                    Console.WriteLine("Pass");
                }else { 
                    Console.WriteLine("Failed"); 
                }
            }catch (Exception ex) {
                Console.WriteLine("No a number!");
            }
        }
    }
}

语句详解

在这里插入图片描述

主要分为三大类:

  • 标签语句

  • 声明语句

  • 嵌入式语句

    例如:if语句中可以嵌套其他语句

    if (3 > 5)
        Console.WriteLine("nihao");//这句话就是嵌套在if语句中的
    

    某些语句(如迭代语句)后面始终跟有一条嵌入式语句。 此嵌入式语句可以是单个语句,也可以是语句块中括在括号 {} 内的多个语句。 甚至可以在括号 {} 内包含单行嵌入式语句。

    if语句就是嵌入式语句中的一种

发现一件神奇的事情

int[] res = [0, 0, 0];
res[^1] = 1;
Console.WriteLine(res[2] == res[^1]);
//res[^1] == res[2]

这是为什么呢?

^:与尾部索引相关(res[res.Length - 1]相当于res[^1]);

声明语句

分为局部变量和局部常量

局部变量声明,即普通的声明

局部常量声明,需要使用const关键字,且声明的时候必须调用初始化器赋值,

不能先声明后面再赋值,而且值也不能再改变

int x = 100;
int x;
x = 100;
/* 这两种声明变量的方式是不一样的
 * 第一种声明变量为变量追加了初始化器
 * 第二种声明变量,没有对变量初始化,然后对变量进行了赋值
 */

表达式语句

在这里插入图片描述

int x;
x = 100;
x++;
/*上面这两个表达式语句都是有值的,但我们需要的只是语句的功能,
 * 这个语句的值我们是不需要的
 * (x = 100)这个语句的值,就是100
 * 但是我们不需要这个值,只是需要将100赋给x的这个功能
 * 而x++,这个语句也是有值的,值就是x,然后才执行自增的功能
 * 但是我们实际上要的也是x自增的功能,不需要x的值
 */

当然也并不是所有表达式计算出来的值都会被丢弃

只返回值的语句是不被允许的

int x = 100;
int y = 10;
x + y;
x == 1;
/* 这种语句是毫无意义的
 * 因为这种语句的功能只有返回一个值,而没有除了返回值以外的其他功能
 * 这种语句在C#中是不被允许的
 * 而在c/c++中可以
 */

块语句

在这里插入图片描述

编译器永远把块语句当做一条完整的语句来看待,不管块里面容纳了多少子语句

块语句实际上就是,大括号里面有多条语句

用于在只允许使用单个语句的上下文种编写多条语句,这句话的意思很简单

if (3 < 5)
    Console.WriteLine("现在只能写一句");
if (3 < 5) {
    Console.WriteLine("现在呢就可以写很多句了");
    Console.WriteLine("现在呢就可以写很多句了");
    Console.WriteLine("现在呢就可以写很多句了");
}
/*
 * {
        Console.WriteLine("现在呢就可以写很多句了");
        Console.WriteLine("现在呢就可以写很多句了");
        Console.WriteLine("现在呢就可以写很多句了");
    }
 * 这个就是块语句
 */

块语句单独用,并不常见。更多的是和其他语句一起用,比如if

{
    int x;
    if (3 < 5)
        Console.WriteLine(123);
    hello: Console.WriteLine("123");
    //这个就是标签语句
    goto hello;//现在这个会造成死循环
}

选择语句

ifif-elseswitch

就只有这三种,用法和Java之类的语言是一样的,因为这个都继承自C/C++。

因为switch不太熟悉,着重写一下

在这里插入图片描述

switch 表达式的值必须是常量 sbytebyteshortushortintuintongulongboolcharstring emm-type,或者是对应于以上某种类型的可以为 null 的类型

case 值标签,作为匹配值。

并且case标签后面所跟的表达式,的值的类型必须和switch后面的表达式的值的类型一致

如果没有匹配的 case 值标签,则使用可选 default 标签作为匹配值

**default 关键字的用途是提供默认操作代码块,无论匹配的 case 值如何,将始终执行该代码块。**这和if-else中的else是类似的

单个 switch 部分可以有多个 case 标签。

break 关键字指示运行时停止计算和阻止执行 switch 构造中的其他 case。

因为switch语句后面要跟一个常量表达式,所以会不太方便。所以使用if-else是多于使用switch

C#中要求,casedefault后面必须带break,不然不能编译通过,更为严谨一些。

而在C中,不带break是可以被允许的

使用示例:

namespace StatementsExample {
    internal class Program {
        static void Main(string[] args) {
            //需求:80~100 ->A; 60~79->B; 40~59 ->C; 0~39->D; 其他->Error
            string str = Console.ReadLine();
            int score = int.Parse(str);
            switch (score /10) {
                case 10://当输入101时,仍然是10,这是有问题的,需要特殊处理
                    if(score == 100) {
                        goto case 8;
                    } else {
                        goto default;
                    }//这样子就没问题了
                case 9:
                case 8:
                    Console.WriteLine("A");
                    break;
                case 7:
                case 6:
                    Console.WriteLine("B");
                    break;
                case 5:
                case 4:
                    Console.WriteLine("C");
                    break;
                case 3:
                case 2:
                case 1:
                case 0:
                    Console.WriteLine("D");
                    break;
                default:
                    Console.WriteLine("Error!");
                    break;
            }
        }
    }
}
int employeeLevel = 100;
string employeeName = "John Smith";

string title = "";

switch (employeeLevel)
{
    case 100:
    case 200:
        title = "Senior Associate";
        break;
    case 300:
        title = "Manager";
        break;
    case 400:
        title = "Senior Manager";
        break;
    default:
        title = "Associate";
        break;
}
Console.WriteLine($"{employeeName}, {title}");

$是一个特殊字符

/* $表示字符串内插
 */
var name = "Mark";
var date = DateTime.Now;
Console.WriteLine("Hello, {0}! Today is {1}, it's {2:HH:mm} now.", name, date.DayOfWeek, date);
Console.WriteLine($"Hello, {name}! Today is {date.DayOfWeek}, it's {date:HH:mm} now.");
/*
 * 使用$字符可以使下面这条语句代替上面那条语句
 */

异常处理语句

包含**throwtry-catchtry-finallytry-catch-finally**

在这里插入图片描述

当使用try-finally语句时,即使出现异常,也不会去捕捉,并且也不会让程序崩溃。

文档中可以查看到,该方法可能会抛出的异常 在这里插入图片描述

throw关键字,抛出异常,谁调用这个方法,谁就来处理这个异常

无论是否发生异常,finally中的内容都会被执行,一般只会在finally中写两类内容:

  1. 释放系统资源的语句

    这样无论是否使用资源,都不会出错

  2. 写程序的执行记录

这样就有助于看清楚,出错的位置,并且提醒输出的值是否是有错的

这个示例很重要,演示了大多数用法

namespace StatementsExample {
    internal class Program {
        static void Main(string[] args) {
            Calculator c = new Calculator();
            int r = 0;
            try {
                r = c.Add("99999999999999999", "100");
            }catch(OverflowException oe) {
                Console.WriteLine(oe.Message);
            }
            
            Console.WriteLine(r);
        }
    }
    class Calculator {
        public int Add(string str1, string str2) {
            int a = 0;
            int b = 0;
            bool hasError = false;

            try {
                a = int.Parse(str1);
                b = int.Parse(str2);
            } catch (ArgumentNullException ane) {//可以抓住异常变量
                Console.WriteLine(ane.Message);
                //于是可以调用这个异常,把异常信息打出来
                hasError = true;

            } catch (FormatException) {
                Console.WriteLine("Your argument(s) are not number");
                hasError = true;

            } catch (OverflowException) {

                throw;
                /* throw关键字,对没有处理的异常,会交给方法的调用者处理
                 * 当没有oe 这个标识的时候,throw仍然可以触发
                 */

            } finally {//无论是否发生异常,finally中的内容都会被执行
                /*一般只会在finally中写两类内容
                 * 1.释放系统资源的语句,
                 *  这样无论是否使用资源,都不会出错
                 * 2. 写程序的执行记录
                 */
                if(hasError) { 
                    Console.WriteLine("Execution has error!");
                } else {
                    Console.WriteLine("Done!");
                }
                /*
                 * 这样子打出log,可以更清晰的看到程序哪个地方出错了
                 */

            }
            /*
             * 精细化的捕捉异常
             * 需要捕捉哪种异常,可以去文档中查对应的方法
             */
            
            int res = a + b;
            return res;
        }
    }
}

平时写代码的时候,如果觉得有问题,一定要使用try语句,养成习惯。

迭代语句

此迭代语句重复执行语句或语句块。 for 语句:在指定的布尔表达式的计算结果为 true 时会执行其主体。 foreach 语句:枚举集合元素并对集合中的每个元素执行其主体。 do 语句:有条件地执行其主体一次或多次。 while 语句:有条件地执行其主体零次或多次。

在迭代语句体中的任何点,都可以使用 break 语句跳出循环。 可以使用 continue 语句进入循环中的下一个迭代。

这四个语句当中,foreach语句最为重要

do语句一定会执行一次循环体中的内容,在某些场合,会比while语句更好用

do语句、break语句和continue语句的示例

int score = 0;
int sum = 0;
do {
    Console.WriteLine("Please input first number");
    string str1 = Console.ReadLine();
    if (str1.ToLower() == "end") {
        break;
    }
    int x = 0; 
    try {
        x = int.Parse(str1);
    } catch {
        Console.WriteLine("First number has problem! Restart.");
        continue;
    }
    

    Console.WriteLine("Please input second number");
    string str2 = Console.ReadLine();
    if (str2.ToLower() == "end") {
        break;
    }
    int y = 0;
    try {
        y = int.Parse(str2);
    } catch {
        Console.WriteLine("Second number has problem! Restart.");
        continue;
    }

    sum = x + y;
    if (sum == 100) {
        score++;
        Console.WriteLine($"Correct! {x} + {y} = {sum}");
        Console.WriteLine($"Your score is {score}");
    } else {
        Console.WriteLine($"Error! {x} + {y} = {sum}");
    }
} while (sum == 100);

Console.WriteLine($"Your score is {score}");

只要是实现了IEnumerable这个接口的,都可以被迭代

在这里插入图片描述

集合遍历的底层逻辑

int[] intArray = { 1, 2, 3, 4, 5 };
IEnumerator enumerator = intArray.GetEnumerator();
/* bool MoveNext()这个方法
 * 意思就是如果这个迭代器,还能向后移动的话,就返回true,否则就返回flase
 * Current() 获取当前访问的元素
 * 
 * Reset()方法,把迭代器的指示箭头拨回到最开始
 */

while (enumerator.MoveNext()) {
    Console.WriteLine(enumerator.Current);
}
/* 1
 * 2
 * 3
 * 4
 * 5
 */
//需要把指针重置才能继续访问
enumerator.Reset();
while (enumerator.MoveNext()) {
    Console.WriteLine(enumerator.Current);
}

foreach语句实际上就是一个迭代器,就是集合遍历的简记法

List<int> intList = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8};
foreach (var current in intList)
{
    Console.WriteLine(current);
}
/* 1
 * 2
 * 3
 * 4
 * 5
 * 6
 * 7
 * 8
 */

跳转语句

跳转语句无条件转移控制。 break 语句将终止最接近的封闭迭代语句switch 语句continue 语句启动最接近的封闭迭代语句的新迭代。 return 语句终止它所在的函数的执行,并将控制权返回给调用方。 goto 语句将控制权转交给带有标签的语句。

goto语句基本不怎么用了。

breakcontinue上面有演示,throw语句在异常处理那里有介绍。

这里着重强调一下return

要尽早return。简单来说就是,需要return的情况,比不需要return的情况少,那么最好把需要return的情况分出来

避免头重脚轻,事实上,很多算法的模板都是这样的,例如:深度优先搜索和广度优先搜索算法。

namespace StatementsExample {
    internal class Program {
        static void Main(string[] args) {
            Greeting("Mr.Ye");
        }
        static void Greeting(string name) {
            if(string.IsNullOrEmpty(name)) {//只有判定某些情况,就立即返回
                return;
            }
            //后面可能会有很大一块逻辑
            Console.WriteLine($"Hello, {name}!");
        }
    }
}

如果方法的返回值,不是void类型的。并且在方法中使用了选择语句,那么需要在选择语句的每一个分支当中都能返回。

namespace StatementsExample {
    internal class Program {
        static void Main(string[] args) {
            string str = WhoIsWho("Is");
            Console.WriteLine(str);
        }

        static string WhoIsWho(string name) {
            if(name == "Mr.Ye") {
                return "Is he";
            }
            else
            {
                return "I don't know";
            }
        }
    }
}

checked/unchecked语句、标签语句、空语句,这三种语句不太重要或不太常用,就不细讲了,具体可以看文档。

using语句、yield语句、look语句(应用于多线程)这三种语句比较难,后面再讲。

($“Hello, {name}!”);
}
}
}




如果方法的返回值,不是`void`类型的。并且在方法中使用了选择语句,那么需要在选择语句的每一个分支当中都能返回。

```c#
namespace StatementsExample {
    internal class Program {
        static void Main(string[] args) {
            string str = WhoIsWho("Is");
            Console.WriteLine(str);
        }

        static string WhoIsWho(string name) {
            if(name == "Mr.Ye") {
                return "Is he";
            }
            else
            {
                return "I don't know";
            }
        }
    }
}

checked/unchecked语句、标签语句、空语句,这三种语句不太重要或不太常用,就不细讲了,具体可以看文档。

using语句、yield语句、look语句(应用于多线程)这三种语句比较难,后面再讲。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值