# 变量和表达式
# 基本语法
C#由一系列语句组成,每条语句都有一个分号结束。C#是一种块结构的语言,块使用花括号来界定,不像Python用缩进界定,代码块可包含任意多行语句。
在C#代码中,有两种注释,代码注释和xml注释。
# 代码注释
//块注释:
/*
comment
...
*/
//行注释:
// comment
2
3
4
5
6
7
8
9
10
# XML注释
参考资料:XML文档注释 (opens new window)。
可以通过这个方式为代码创建文档:将特殊注释字段中的XML元素包含在源代码中注释引用的代码块前面,使用-doc选项进行编译时,编译器会在源代码中搜索所有XML标记,并创建一个XML文档文件,若要基于编译器生成的文件创建最终文档,可以使用DocFX (opens new window)和Sandcastle (opens new window)。
//单行分隔符
///
//多行分隔符
/** <summary>text</summary> */
/**
<summary>text</summary>
*/
/**
* <summary>text</summary>
*/
2
3
4
5
6
7
8
9
10
11
12
13
C#代码区分大小写,下面的代码都不能正常工作:
console.WriteLine("hello world");
Console.Writeline("hello world");
2
# 控制台应用基本结构
using System;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Console.WriteLien("hello world");
Console.ReadKey();
}
}
}
2
3
4
5
6
7
8
9
10
11
12
# 预处理指令
同C++一样,C#也有很多预处理指令,比如#region和#endregion,详情请看C# 预处理器指令 (opens new window)。
# 变量
假如把计算机内存比作成一个架子,那么变量就是架子上的盒子,而数值字符等就是盒子中的内容,因为有很多类型的数值,如果放在一种类型的盒子中,比如较小类型的数值放在较大类型数值所在的盒子中,会浪费空间,这就产生了很多种变量类型,也就说有很多种盒子。
声明C#的变量语法:
type name;
# 简单类型
简单类型是组成应用程序中的基本构件类型,比如数值和布尔值,同复杂类型不一样,简单类型没有子类型或特性。
C#中的每一种类型都利用了 .Net Framework中定义的标准类型,使用标准类型可以在语言之间交互操作,在C#中这些类型的名称是Framework中定义的类型的别名。下面来看下它们的类型:
# 整数类型
类型 | 别名 | 允许的值 |
---|---|---|
sbyte | System.SByte | -2^7 ~ 2^7-1 |
byte | System.Byte | 0 ~ 2^8-1 |
short | System.Int16 | -2^15 ~ 2^15-1 |
ushort | System.UInt16 | 0 ~ 2^16-1 |
int | System.Int32 | -2^31 ~ 2^31-1 |
uint | System.UInt32 | 0 ~ 2^32-1 |
long | System.Int64 | -2^63 ~ 2^63-1 |
ulong | System.UInt64 | 0 ~ 2^64-1 |
"u"是unsigned的缩写,表示不能在这些类型的变量中存储负数,这样就把符号位用作数值存储位,比有符号数多存储一倍。
# 浮点类型
浮点类型可以用 m*2^e形式来表示:
类型 | 别名 | m的最小值 | m的最大值 | e的最小值 | e的最大值 | 近似值的最小值 | 近似值的最大值 |
---|---|---|---|---|---|---|---|
float | System.Single | 0 | 2^24 | -149 | 104 | 1.5*10^-45 | 3.4*10^38 |
double | System.Double | 0 | 2^53 | -1075 | 970 | 5*10^-324 | 1.7*10^308 |
decimal | System.Decimal | 0 | 2^96 | -28 | 0 | 1*10^-28 | 7.9*10^28 |
# 文本和布尔类型
类型 | 别名 | 允许的值 |
---|---|---|
char | System.Char | 一个Unicode字符,存储0~2^16-1直接的数 |
bool | System.Boolean | 布尔值:true或false |
string | System.String | 一个字符序列 |
string的字符数量是没有上限的,因为它可以使用可变大小的内存。
# Nullable类型
在 .Net 2.0中,C#提供了一种Nullable类型,允许定义值类型包含null值,这对于处理数据库可空字段有很大的帮助,具体就是在定义变量时类型名后面添加?。
还提供了??运算符,当被操作的变量为空值时,表达式返回??后面的值,可以利用这点给变量初始化。
static void Main(string[] strs)
{
int? intVal = null;
Console.WriteLine($"intVal.HasValue:{intVal.HasValue}");
int intVal2 = intVal ?? -1;
Console.WriteLine($"intVal2:{intVal2}");
int intVal3 = intVal.GetValueOrDefault();
Console.WriteLine($"intVal3:{intVal3}");
//Console.WriteLine($"intVal.HasValue:{intVal.Value}");
intVal = 2;
Console.WriteLine($"intVal.HasValue:{intVal.HasValue}");
}
2
3
4
5
6
7
8
9
10
11
12
13
注意:
- 语法T?是System.Nullable<T>的简写,此处T为值类型,这两种形式可以互换。
- 如果变量已被赋值,则Value属性返回值,如果没有被赋值,其值为null值,则抛出System.InvalidOperationException异常。
# Nullable运算符
public class Person
{
public int Age;
public int GetAge()
{
return Age;
}
}
static void Main(string[] args)
{
int? org=1;
int result = (int)org * 2;
Console.WriteLine(result);
//上面没有显式转换为int的话是编译不过去的。如果org为null,会抛出System.InvalidOperationException异常。
int? org2 = 2;
int result2 = org2.Value * 2;
Console.WriteLine(result2);
int? org3 = 3;
int? result3 = org3 * 2;
Console.WriteLine(result3.Value);
bool? bool1 = true;
bool? bool2 = false;
//bool1* bool2;
int? org4 = null;
int result4 = org4 * 2 ?? 5;
Console.WriteLine(result4);
Person p1 = null ;
Console.WriteLine(p1?.GetAge());
//var onChanged = onChanged;
//if (onChanged != null)
//{
// onChanged(this, args);
//}
//上面检查事件是否为空的代码可用下面一行表示。
//onChanged?.Invoke(this, args);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
注意:
- 可空类型赋值给非可空类型是需要显式转换的。
- ??运算符为空接合运算符(null coalescing operator),是一个二元运算符,允许给可能等于null的表达式提供另一个值。
- ?.运算符通常称为Elvis运算符或空条件运算符,有助于避免繁杂的空值检查造成的代码歧义,它还可用于检查事件是否为空。
# 变量的命名
基本的命名规则如下:
- 变量名的第一个字符必须是字母,下划线_或@
- 其后的字符可以是字母,下划线或数字。
# 字面值
有很多后缀可以添加在值的后面来表示值的类型:
类型 | 类别 | 后缀 | 示例 |
---|---|---|---|
bool | 布尔 | 无 | true或false |
int,uint,long,ulong | 整数 | 无 | 100 |
uint,ulong | 整数 | u或U | 100U |
long,ulong | 整数 | l或L | 100L |
ulong | 整数 | ul,uL,Ul,UL,lu,lU,Lu,或LU | 100UL |
float | 实数 | f或F | 1.5F |
double | 实数 | 无,d或D | 1.5 |
decimal | 实数 | m或M | 1.5M |
char | 字符 | 无 | 'a'或转义序列 |
string | 字符串 | 无 | "a...a",或包含转义序列 |
上面整数的排列顺序意为解析的优先级,比如一个没有后缀的整数会根据大小先被解析为int,如果超出范围解析为uint,还超出范围解析为long,次之为ulong。
# 整数
整数分为二进制,八进制,十六进制,他们的前缀依次为0b,0,0x。
当字面值为二进制时阅读很费劲,C#提出了数字分隔符,它还可用于十进制,浮点数和双精度,就是使用一个下划线作为分割符:
float one = 3.14_15_926_3;
int two = 0b0002_0001;
2
# 字符串字面值
有一些字符在前面加\后会有一些其他含义,这些字符可能会直接表示特殊数据或某些功能。
转义序列 | 产生的字符 | 字符的Unicode值 |
---|---|---|
' | 单引号 | 0x0027 |
" | 双引号 | 0x0022 |
\ | 反斜杠 | 0x005C |
\0 | null | 0x0000 |
\a | 警告有蜂鸣声 | 0x0007 |
\b | 退格 | 0x0008 |
\f | 换页 | 0x000C |
\n | 换行 | 0x000A |
\r | 回车 | 0x000D |
\t | 水平制表符 | 0x0009 |
\v | 垂直制表符 | 0x000B |
可以直接使用转义序列或者前缀'\u'加上十六进制的Unicode值,下面两种形式是等价的:
"Hello\nWorld"
"Hello\u000AWorld"
2
当一个字符串中有很多转义字符的时候,比如路径需要很多的反斜杠,如果此时要把所有反斜杠转义会很麻烦,C#中可以在字符串前面加@符号,让所有的字符都不转义,这样会方便一点。
@"hello
world"
@"C:\temp\list\myfile.txt"
2
3
4
# 表达式
将变量和字面量与运算符结合起来,就可以创建表达式,它是计算的基本构件。它有两大类:一个是数学运算符,除了加,减,乘,除,取余,还有自增自减。其中"+"能用于字符串连接;另一类是赋值运算符,即"=","+=","-="等。
# 布尔逻辑
布尔类型有true或false两种,布尔比较需要使用布尔比较运算符,它们有==,!=,<,>,<=,>=。布尔比较直接返回布尔类型:
bool isTrue;
isTrue = myBool == true;
2
有四个条件布尔运算符,就是&,|,&&,||。&&和||同&和|的差别就是如果第一个条件能判断出结果,就不会比较第二个条件。比如&&运算符,如果第一个条件为false,那么它直接返回false。
# 布尔赋值运算符
布尔类型有三个赋值运算符,分别是&=,|=和^=。&=和|=赋值运算符并不是&&和||条件布尔运算符,而是使用&和|。
# 运算符优先级
运算符 |
---|
++和--(前缀),+(一元),-(一元),(),!,~ |
*,/,% |
+(二元),-(二元) |
<<,>> |
<,>,<=,=> |
==,!= |
& |
^ |
| |
&& |
|| |
=,*=,/=,%=,+=,-=,<<=,=>>,&=,^=,|= |
++和--(后缀) |
这些运算符从上往下优先级依次降低。