本篇博客延续前三篇的写作方式,因为内容较为基础,只是以冷热知识的形式列出旧有的容易忽视的C#特性及新出现的特性,当然对一些之前较为模糊的概念也予以整理。
概念重现
1,操作符的概念,操作符分为一元、二元、三元三大类,分别对应一、二、三个操作数,分别包含如下的逻辑:
- 一元正负操作符,表示数值的正负
- 二元算术操作符,包括+,-,*,/,%等。
- 三元条件操作符,包括?:两个操作符隔着三个操作数
2,代码块、作用域和声明空间的区别:声明空间是具名事物的逻辑容器,代码块是两个大括号之间的多条语句,而作用域表示声明的具名事物的作用范围。作用域决定一个名称引用什么事物,而声明空间决定同名的两个事物是否冲突。
3,switch语句使用的时候有如下规范:
- switch 语句中的 expression 必须是一个整型或枚举类型,或者是一个 class 类型,其中 class 有一个单一的转换函数将其转换为整型或枚举类型。
- 在一个 switch 中可以有任意数量的 case 语句。每个 case 后跟一个要比较的值和一个冒号。
- case 的 constant-expression 必须与 switch 中的变量具有相同的数据类型,且必须是一个常量。
- 当被测试的变量等于 case 中的常量时,case 后跟的语句将被执行,直到遇到 break 语句为止。当遇到 break 语句时,switch 终止,控制流将跳转到 switch 语句后的下一行。
- 不是每一个 case 都需要包含 break。如果 case 语句为空,则可以不包含 break,控制流将会 继续 后续的 case,直到遇到 break 为止。
- C# 不允许从一个开关部分继续执行到下一个开关部分。如果 case 语句中有处理语句,则必须包含 break 或其他跳转语句,否则会编译失败
- 一个 switch 语句可以有一个可选的 default case,出现在 switch 的结尾。default case 可用于在上面所有 case 都不为真时执行一个任务。default case 中的 break 语句不是必需的。
- C# 不支持从一个 case 标签显式贯穿到另一个 case 标签。如果要使 C# 支持从一个 case 标签显式贯穿到另一个 case 标签,可以使用 goto 一个 switch-case 或 goto default。
4,C#的预编译指令,和常量以及常量表达式一样,C#预编译指令在编译时调用:
使用的时候一般是用#define定义一个值,然后依据定义来编译代码:
#define PI using System; namespace PreprocessorDAppl { class Program { static void Main(string[] args) { #if (PI) Console.WriteLine("PI is defined"); #else Console.WriteLine("PI is not defined"); #endif Console.ReadKey(); } } }
输出为:
PI is defined
可以用if debug来确定调试时运行哪些代码:
public static void Main(string[] fileName) { #if DEBUG System.Console.WriteLine("只有debug时候使用"); #endif }
C#冷知识
1,在C#中,只有赋值、调用、递增、递减、 await(用于异步编程) 和对象创建表达式才能作为独立语句使用。
2,再来梳理一遍操作符优先级吧!从上到下可以按照如下方式归类:
- 主要运算符:有++、–、()、+、-、!、~。这一级中都是单元运算符,除了其中那一对特殊的具有改变任何运算优先级的括号。这此可以看出,在定义表达式中,那些单元运算符的优先级是很高的,可能是因为它们都直接作用于操作数吧。
- 算术运算符:在常规算术运算符中,有我们经常使用的*、/、%、+、-,因为他们使用得较多,所以也排在较高的位置。这一级是两个特殊的位操作符,<< 和 >>,它们是所有二元操作符中除常规运算符外优先级最高的了,可能是因为与比较运算符、逻辑运算符比起来,这一组操作符进行的还是数值的计算。
- 比较运算符:包括<、>、<=、>=、==、!=,一共六个,这里有一个特点,就小于优先于大于。
- 逻辑运算符,逻辑运算符原本有四种,但用于单元的“非”运算符排在了前面,所以这里面就只有&、^、|,再加上两个补充的用于提高代码效率的运算符&&、||等共有五个。
- 赋值运算符,这一级最多,几乎前面出现过的二元运算符,在这里加上个“=”号就成为一个赋值运算符。首先当然最基本的赋值运算符“=”;然后是常规算术运算符演变来的“*=、/=、%=、+=、-=”,它们的顺序和常规算术符的顺序一样;然后是位移运算符和逻辑运算符,也按它们演变前的顺序排列,为“<<=、>>=、&=、^=、|=”。因为比较运算符所产生的结果的数据类型和它的运算数的数据类型不同,所以它们没有相应的赋值运算符。
3,C#的大多数操作符是左结合的,而赋值操作符是右结合的。
4,在算术运算符中使用字符:先把字符转为数值,得到数值后再转为字符。
public static void Main() { int n = '3' + '4'; char c = (char)n; System.Console.WriteLine(c); //g }
5,结合性和优先级,结合性决定相似操作符的执行顺序,优先级决定不相似操作符的执行顺序,而操作数总是从左向右求值,操作符的结合性和优先级只影响操作符自身的执行顺序,不影响操作数的求值顺序
6,C#禁止一个代码块中声明的局部变量在其子代码块中重复声明。
7,&&和||是短路运算符,只要左边第一个表达式满足条件,就不会继续对右边求值。
8,事先知道循环次数使用for循环,事先不知道使用while。
9,goto语句可以使用lable的方式执行:
myLabel: System.Console.WriteLine("if条件要求为bool类型哦"); goto myLabel;
但是需要注意的是,goto只能跳转到自己上级代码块和同级代码块,不能跳到自己的子代码块,而且要避免使用goto语句。
C#新知识
1,var temp=++count
是先给count自增再赋值,var temp=count++
是先给temp赋值再自增count,需要注意的是,这个操作不是原子操作,是线程不安全的!
2,空合并操作符:如果fileName为null,就使用tml,也就是如果这个值为null,就使用另一个。
public static void Main() { string fileName = null; string fullName = fileName ?? "tml"; }
3,空条件操作符:调用成员前先进行空检查,需要注意,int类型的话返回的是int?,数值类型都类似。
public static void Main(string[] fileName) { var length = fileName?.Length; }
需要注意的是,if条件操作符要求的条件必须为bool类型,所以要结合空合并操作符使用
public static void Main(string[] fileName) { if (fileName[0]?.StartsWith("") ?? false) { System.Console.WriteLine("if条件要求为bool类型哦"); } }
4,foreach用于循环遍历数据项集合的每一项,保证不发生越界错误!foreach循环期间,禁止修改循环变量(数据项)以及数据集合
5,C#7.0开始,支持模式匹配,switch里的每个case不一定是相同类型啦!
public static double ComputeAreaModernSwitch(object shape) { switch (shape) { case Square s: return s.Side * s.Side; case Circle c: return c.Radius * c.Radius * Math.PI; case Rectangle r: return r.Height * r.Length; default: throw new ArgumentException( message: "shape is not a recognized shape", paramName: nameof(shape)); } }