跳至正文

第2章 使用变量、操作符和表达式

  • 棱镜 
  • C#

在这一章,你将学习最基础的C#语法。

理解语句

啥是语句?

语句是执行操作的命令。上一章中的 Console.WriteLine("Hello, world!"); 就是一个语句。棱镜爬也可以是一个语句(你最好不要这么做)。诸如计算值、存储结果,或者向用户输出信息一类的操作都可以是语句。通过按各种方式组合语句,我们可以创建方法。在下一章我们会具体介绍方法,在这里,我们暂时将方法视为具有名字的一串语句。上一章中的 Main() 就是包含一条语句的方法。

语法和语义

C#的语句遵循一个规则集,它们限制语法的格式和构成,统称为语法。对应的,用于描述语句做什么的规范统称为语义。最简单也是最重要的一条语法规则——所有语句必须以分号 ; 终止,正如上一章所演示的,删除分号将导致报错。

此外,C#是“自由格式”的,所有的空格、换行符都无实际意义,你可以采取自己喜欢的样式安排代码的布局。但需要注意的是,简单、统一的布局样式会使得程序更易阅读和理解。VS在编写代码时会提供自动缩进,正是出于此种目的。

使用标识符

认识标识符

标识符是对程序中各个元素进行标识的名称,简单来讲就是元素的名字。命名空间的名字是标识符,类的名字是标识符,方法的名字是标识符,很快就要讲到的变量名也是标识符,小裙子也可以是标识符

使用标识符时必须遵循下面的语法规则:

  • 只能使用字母、数字、下划线例如,football , foot_ball , football9 是合法的, foot$ball 是非法的。注意,尽管使用中文标识符不会报错,但由此产生的一切后果将由你自行承担,包括但不限于程序莫名崩溃,提问的时候被群友骂
  • 标识符不能以数字开头例如,1football 是非法的。
  • 区分大小写例如,Football , football , FoOtbAll 是三个不同的标识符(你最好不要写成第三个)

认识关键字

C#有77个标识符仅供自己使用,你不能出于自己的目的使用这些标识符,它们统称为关键字,每个关键字都有特定的含义。关键字的例子包括 class , namespace , using 等,完整的关键字列表可以自行查找,在后面的教程中我们也会学习许多关键字的含义和用法。

此外,下面的关键字虽然可以用于声明标识符,但它们在一些上下文中被C#使用,因此通常不建议用作自己的标识符

add , get , remove , alias , global , select , ascending ,
group , set , async , into , value

使用变量

啥是变量?

变量是容纳值的一个存储位置,更通俗地说,变量是可以变动的数据。你可以把变量想象成内存中容纳临时信息的容器。程序的每个变量在其使用范围(也就是作用域,在第三章中会介绍)内必须有无歧义的名称。

声明和赋值变量

众所周知,在释放技能前必须大声喊出技能名,使用变量前也必须声明变量。例如我们可以使用下面的代码声明一些变量:

int age;
float val;
string text;

int , float , string 都是C#的基元数据类型,马上就会介绍。它们表示了变量的类型。

这些都是合法的语句,功能是声明变量。 age , val , text 可以是任何合法的标识符,但你最好不要用 aaabbbcccdddnum 这样的东西。

仅仅声明变量是没有用的,使用变量必然涉及到对变量的赋值操作,例如:

int age;
age = 18;

其中, =赋值操作符,它把右侧的值赋给左边的变量。这两行语句声明变量 age 并将其赋值为18。

当然,C#中也可以合并变量声明和赋值,就像这样:

int age = 18;

使用变量

赋值后,你可以在代码中使用变量名来引用变量容纳的值,例如,你可以用下面的代码输出 age 的值:

Console.WriteLine(age);

你可以在需要的时候再次改变变量的值,也可以把变量的值赋给另一个变量:

int a = 3, b = 2;
//这行语句同时声明a, b两个变量,并将a赋值为3,b赋值为2
Console.WriteLine(a);
a = 4;
Console.WriteLine(a);
a = b;
Console.WriteLine(a);

使用基元数据类型

认识基元数据类型

C#内建许多基元数据类型(又称为基本数据类型或原始数据类型),这篇文章中有较详细的介绍,在这里就不过多赘述了。

明确赋值规则

C和C++的许多bug都是由于误用了未赋值的局部变量,而C#中不允许使用未赋值的变量,在使用变量前,它必须被明确赋值。这就是C#的明确赋值规则

例如,下面的代码将会造成编译错误,因为age尚未赋值:

int age;
Console.WriteLine(age);

有时类中的字段可以在显式赋值前使用,在此处暂时不介绍。

使用算术操作符

认识操作符

那些我们胎教小学就学过的加、减、乘、除在C#中也有对应的运算符 + , - , * , / ,还有取模运算符 % ,它们对值进行“操作”或“运算”并返回新的值。这些符号又称为操作符。在下面的例子中,我们使用操作符的运算结果为变量赋值:

int a = 2 * 2 + 2;
//a的值是6

注意,一个运算并不能单独成为一个语句,例如,下面的代码会编译错误:

114514 * 1919810;
//homo特有的无意义语句(恼)

因为这样的语句是没有意义的,要让它成为语句,你需要将它的结果赋值给某个变量(也就是写赋值语句),或者用作某个方法的参数(后面再讲)。

操作符和类型

基元数据类型可不止有数字,而各个操作符对于不同的类型的运算效果可能是不同的,也并不是所有的操作符都支持所有的数据类型。

我们使用下面的例子帮助你来理解:

int a = 3, b = 2;
Console.WriteLine(a/b); //输出为1
float c = 3f, d = 2f;
Console.WriteLine(c / d); //输出为1.5

ab 均为 int 类型的操作数,得到的结果也是 int ,在这种情况下,C#总是对结果进行向下取整。而 cdfloat 类型,因此结果也是 float 。你可以混用不同的数据类型,例如 5 / 2.0 是合法的,因为C#编译器在检测到类型不一致的情况时会自动生成代码将 int 转换为 double ,所以结果是 double 类型的 2.5 。尽管合法,但隐式的数据转换有时会出现预料之外的行为,因此我不建议你这样做。

% 是取模运算符, x % y 表示用 x 除以 y 的余数,例如 9 % 2 的结果是 1 ,C#中也支持对 doublefloat 使用取模运算,结果也不一定是整数,具体的运算规则可以自行查找相关资料。

string 类型只支持 + 操作符,它的含义是连接两个字符串,而 -,*,/ 等对字符串是没有意义的。

注意,以下代码的输出结果是 111222 ,而不是 333 ,因为 text1text2 都是 string 类型的操作数:

string text1 = "111", text2 = "222"
Console.WriteLine(text1 + text2);
//输出为111222

如果你希望将被存储为文本的数字转换为 int ,你可以使用 int.Parse() 之类的方法将其转换为 int 类型的操作数,在这里暂不介绍。

当然,为了连接字符串,我们也有更好的方法——字符串插值。这项功能在C#6推出,它比使用 + 高效得多。如果没有特殊目的,应当尽量使用字符串插值来连接字符串。通常,连接字符串是为了将变量的值包含到字符串中:

int age = 18;
Console.WriteLine("Your age is: " + age);

有了字符串插值后,我们可以这样写:

int age = 18;
Console.WriteLine($"Your age is: {age}"); 
//输出: 
//Your age is: 18
Console.WriteLine("Your age is: {age}");
//输出:
//Your age is: {age}

字符串开头的 $ 表示该字符串为插值字符串, {} 之间的任何表达式都会被求值后嵌入到字符串中。如果删去 $ 符号, {age} 会按字面值处理。

控制优先级

众所周知,四则运算中 */ 的优先级高于 +- 。对于表达式 2 + 3 * 4,如果没有优先级规则,它将是有歧义的。我们规定优先级后可以计算该表达式的值为 14,而不是 20

Console.WriteLine(2 + 3 * 4);
//输出14,而不是20

此外,你也可以像现实中一样,使用小括号来覆盖优先级。注意,只有小括号用来覆盖优先级,方括号和大括号都有其他的含义,并不用作此目的。例如下面的代码:

Console.WriteLine((2 + 3) * 4);
//输出20,而不是14

无论多复杂的表达式你都只能用小括号和嵌套的小括号,比如下面的表达式:

Console.WriteLine((3 + (4 - 2) * 5) / 2);
//输出是多少呢?

以后讨论新的操作符时都会指出它的优先级。

表达式的结合性

我们学过加法和乘法的结合律。C#中,在遇到相同优先级的多个操作符时怎么处理?这时就要用到结合性的概念。结合性是指操作数的求值方向(向左或者向右),例如表达式 4 / 2 * 6 将从左到右计算,因为 */ 都是左结合性的。

说到结合性,我们必须讨论赋值运算符 = 的特殊性,它是右结合性的,它将右边的操作数求值,然后将结果保存到左边的操作数中,同时返回该值作为运算结果。下面的代码演示了它的性质:

int a;
int b = a = 10 * 10;
//除非你知道你在干什么,否则我不建议你在实际的代码中这样写
Console.WriteLine(a);
Console.WriteLine(b);

在上面的例子中, 10 * 10 被计算后赋值给 a,返回的结果 100 作为右操作数赋值给 b

但需要注意的是,下面代码可能并不像你想象的那样工作:

int a, b, c = 10;
Console.WriteLine(a + b + c);
//编译时错误,报错是什么呢?

VS会提示a和b是未赋值的局部变量。

递增和递减运算符

如果我们要让变量的值加1,我们可以使用下面的语句:

int a = 0;
a = a + 1;
//其实写成a += 1也可以...

但让变量加1这样的事情实在是太普遍了,C#为了方便大家偷懒专门提供了 ++-- 操作符,例如刚刚的第二行和下面这一行完全等价:

a++;
//简洁!

++--一元操作符,即只有一个操作数,它们具有相同的优先级和左结合性。

它们具有一个独特的性质:既可以放在变量前,称为前缀形式,也可以放在变量后,称为后缀形式。如果是单独的一行语句(就像上面那样),这两种形式没有区别。但当你用到这两种操作符的运算结果时,它们就会有所不同,例如:

int x = 42;
Console.WriteLine(x++);
//先执行该语句,然后x++
//输出是42,但这一行执行后x的值是43
Console.WriteLine(++x);
//先++x,然后执行该语句
//输出是44,x的值也被赋为44

其实很好记,只要看表达式中各个元素的顺序即可。在表达式 x++ 中,变量 x 先出现,所以先返回它现在的值再递增;而在表达式 ++x 中,递增操作符先出现,所以先递增,再返回递增后的值。

whiledo 等语句经常使用递增和递减等操作符,后面的章节会详细介绍。如果只是将递增和递减表达式作为单独的语句使用,请尽量使用后缀形式(用前缀其实也不会被人打啦)。

声明隐式类型的局部变量

在前面介绍过,我们可以在同一个语句中声明并初始化变量:

int myInt = 99;

现在,我们介绍 var 关键字,它用于声明隐式类型的局部变量。

var a = 99;
var text = "Hello";

如果你善用VS,将光标悬停在变量名上,你可以看到变量的实际类型:

var 的作用是让编译器根据用于初始化变量的值自动推断变量类型。需要注意的是,它只是在声明变量时提供一些简单的写法(比如帮你简写一些超长的类型名),一旦声明,变量的类型便固定了,例如,刚刚的例子中,下面的语句会发生编译错误:

a = "text"

此外,在声明匿名类型时 var 关键字也会发挥作用,在此处暂不赘述。

注意,不要滥用var,由此产生的提问时被群友打等后果请自行承担。

在下一章中,我们将正式学习方法和作用域的概念,并学习如何使用VS对程序进行调试。

第3章 方法和作用域

标签:

发表回复