在这一章,你将学习最基础的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
a
和 b
均为 int
类型的操作数,得到的结果也是 int
,在这种情况下,C#总是对结果进行向下取整。而 c
和 d
为 float
类型,因此结果也是 float
。你可以混用不同的数据类型,例如 5 / 2.0
是合法的,因为C#编译器在检测到类型不一致的情况时会自动生成代码将 int
转换为 double
,所以结果是 double
类型的 2.5
。尽管合法,但隐式的数据转换有时会出现预料之外的行为,因此我不建议你这样做。
%
是取模运算符, x % y
表示用 x
除以 y
的余数,例如 9 % 2
的结果是 1
,C#中也支持对 double
和 float
使用取模运算,结果也不一定是整数,具体的运算规则可以自行查找相关资料。
而 string
类型只支持 +
操作符,它的含义是连接两个字符串,而 -
,*
,/
等对字符串是没有意义的。
注意,以下代码的输出结果是 111222
,而不是 333
,因为 text1
和 text2
都是 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
中,递增操作符先出现,所以先递增,再返回递增后的值。
while
和 do
等语句经常使用递增和递减等操作符,后面的章节会详细介绍。如果只是将递增和递减表达式作为单独的语句使用,请尽量使用后缀形式(用前缀其实也不会被人打啦)。
声明隐式类型的局部变量
在前面介绍过,我们可以在同一个语句中声明并初始化变量:
int myInt = 99;
现在,我们介绍 var
关键字,它用于声明隐式类型的局部变量。
var a = 99; var text = "Hello";
如果你善用VS,将光标悬停在变量名上,你可以看到变量的实际类型:
var
的作用是让编译器根据用于初始化变量的值自动推断变量类型。需要注意的是,它只是在声明变量时提供一些简单的写法(比如帮你简写一些超长的类型名),一旦声明,变量的类型便固定了,例如,刚刚的例子中,下面的语句会发生编译错误:
a = "text"
此外,在声明匿名类型时 var
关键字也会发挥作用,在此处暂不赘述。
注意,不要滥用var,由此产生的提问时被群友打等后果请自行承担。
在下一章中,我们将正式学习方法和作用域的概念,并学习如何使用VS对程序进行调试。