数据类型
在 C# 中,变量分为以下类型:
值类型
值类型复制时直接拷贝。如果值类型的数据成员中包括引用类型时,引用类型只会浅拷贝。
T?
是一个可以为 T
或者=null= 的值类型,类似 optional
。
简单类型
C# 内置了下面几种类型,也叫做简单类型:
对于简单类型,支持下面的操作:
- 支持字面量声明值,如 ‘A’ 是 char 类型的字面量
- 可以用
const
声明简单类型常量,其它的类型不行 - 简单类型的常量表达式在编译期执行
C# 的 char 类型是 Unicode UTF-16 ,每个 char 大小为 16 bit
枚举类型
1
2
3
4
5
6
| enum Season {
Spring,
Summer,
Autumn,
Winter
}
|
也可以指定枚举成员的具体类型
1
2
3
4
5
6
| enum Season : ushort {
Spring,
Summer,
Autumn,
Winter
}
|
更多请看:枚举类型
结构体类型
结构体是值类型,可以有数据和方法, 相关文档:结构体
1
2
3
4
5
6
7
8
9
10
11
12
13
| public struct Coords
{
public Coords(double x, double y)
{
X = x;
Y = y;
}
public double X { get; }
public double Y { get; }
public override string ToString() => $"({X}, {Y})";
}
|
readonly
结构体,结构体的所有成员是不可变的。
1
2
3
4
5
6
7
8
9
10
11
12
13
| public readonly struct Coords
{
public Coords(double x, double y)
{
X = x;
Y = y;
}
public double X { get; init; }
public double Y { get; init; }
public override string ToString() => $"({X}, {Y})";
}
|
方法也可以称为 readonly
的, 重载的方法也可加 readonly
1
2
3
4
5
6
| public readonly double Sum()
{
return X + Y;
}
public readonly override string ToString() => $"({X}, {Y})";
|
Tuple 类型
相关文档:Tuple 类型
1
2
3
4
5
6
7
8
9
| (double, int) t1 = (4.5, 3);
Console.WriteLine($"Tuple with elements {t1.Item1} and {t1.Item2}.");
// Output:
// Tuple with elements 4.5 and 3.
(double Sum, int Count) t2 = (4.5, 3);
Console.WriteLine($"Sum of {t2.Count} elements is {t2.Sum}.");
// Output:
// Sum of 3 elements is 4.5.
|
Nullable 类型
类型 Optional, 值可以为 null ,相关文档:Nullable
1
2
3
4
5
6
7
8
9
10
| double? pi = 3.14;
char? letter = 'a';
int m2 = 10;
int? m = m2;
bool? flag = null;
// An array of a nullable value type:
int?[] arr = new int?[10];
|
可以用 is
或者 hasValue
检测值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| int? a = 42;
if (a is int valueOfA)
{
Console.WriteLine($"a is {valueOfA}");
}
else
{
Console.WriteLine("a does not have a value");
}
// Output:
// a is 42
int? b = 10;
if (b.HasValue)
{
Console.WriteLine($"b is {b.Value}");
}
else
{
Console.WriteLine("b does not have a value");
}
// Output:
// b is 10
|
引用类型
引用类型的存储数据的引用,赋值时执行浅拷贝。
C# 中可以声明引用类型的关键字:class, interface, delegate, record
使用 class
关键字声明类:
1
2
3
4
5
| class TestClass
{
// Methods, properties, fields, events, delegates
// and nested classes go here.
}
|
使用 interface
定义接口约束:
1
2
3
4
5
6
7
8
9
10
11
12
13
| interface ISampleInterface
{
void SampleMethod();
}
class ImplementationClass : ISampleInterface
{
// Explicit interface member implementation:
void ISampleInterface.SampleMethod()
{
// Method implementation.
}
}
|
使用 record
定义一个引用类型,编译器会为它提供一些默认的机制,如相等比较是值相等,定义输出格式。
1
2
3
4
5
| public record Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
};
|
内置 object 类型
object
是 System.Object
的别名,在 C# 的类型系统中,所有类型都直接或间接继承自 object
。
当一个值类型转换成 object
类型,叫装箱,当一个 object
类型的值转成值类型,叫拆箱。
string 类型
string
类型是 System.String
的别名,string 是引用类型,但是它的 \=\= 和 !\= 操作是比较值
1
2
3
4
5
6
| string a = "hello";
string b = "h";
// Append to contents of 'b'
b += "ello";
Console.WriteLine(a == b);
Console.WriteLine(object.ReferenceEquals(a, b));
|
string
对象内的字符串是不可变的,用 +
会创建新的 string
对象。
1
| string a = "good " + "morning";
|
[]
操作符可以访问字符串中的字符。
1
2
| string str = "test";
char x = str[2]; // x = 's';
|
字符串字面量前加 @ 就是原生字符串,内部可以不用转义。
1
| @"c:\Docs\Source\a.txt" // rather than "c:\\Docs\\Source\\a.txt"
|
如果字符串内部有双引号,用两个双引号表示。
1
| @"""Ahoy!"" cried the captain." // "Ahoy!" cried the captain.
|
delegate 类型
delegate
类型的声明类似方法签名,它有返回值和任意数量的参数。
1
2
| public delegate void MessageDelegate(string message);
public delegate int AnotherDelegate(MyType m, long num);
|
一个 delegate
是引用类型,可以用来表示命名或者匿名方法,类似 C++ 中的函数指针。
dynamic 类型
dynamic
类型的变量可以绕过编译期类型检查,而在运行时解析。
指针类型
指针类型只能在 unsafe
代码中使用。
1
2
| type* identifier;
void* identifier; //allowed but not recommended
|
控制流程
条件判断
使用 if-else 语句
1
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
| Console.Write("Enter a character: ");
char ch = (char)Console.Read();
if (Char.IsUpper(ch))
{
Console.WriteLine("The character is an uppercase letter.");
}
else if (Char.IsLower(ch))
{
Console.WriteLine("The character is a lowercase letter.");
}
else if (Char.IsDigit(ch))
{
Console.WriteLine("The character is a number.");
}
else
{
Console.WriteLine("The character is not alphanumeric.");
}
//Sample Input and Output:
//Enter a character: E
//The character is an uppercase letter.
//Enter a character: e
//The character is a lowercase letter.
//Enter a character: 4
//The character is a number.
//Enter a character: =
//The character is not alphanumeric.
|
可以使用 switch 语句
1
2
3
4
5
6
7
8
9
10
11
12
| switch (caseSwitch)
{
case 1:
Console.WriteLine("Case 1");
break;
case 2:
Console.WriteLine("Case 2");
break;
default:
Console.WriteLine("Default case");
break;
}
|
循环语句
for
语句
1
2
3
4
5
6
| for (int i = 0; i < 3; i++)
{
Console.Write(i);
}
// Output:
// 012
|
foreach
语句
1
2
3
4
5
6
7
| var fibNumbers = new List<int> { 0, 1, 1, 2, 3, 5, 8, 13 };
foreach (int element in fibNumbers)
{
Console.Write($"{element} ");
}
// Output:
// 0 1 1 2 3 5 8 13
|
await foreach
语句,消费异步数据流
1
2
3
4
| await foreach (var item in GenerateSequenceAsync())
{
Console.WriteLine(item);
}
|
使用 var
在编译器推导类型。
do while
语句
1
2
3
4
5
6
7
8
| int n = 0;
do
{
Console.Write(n);
n++;
} while (n < 5);
// Output:
// 01234
|
while
语句
1
2
3
4
5
6
7
8
| int n = 0;
while (n < 5)
{
Console.Write(n);
n++;
}
// Output:
// 01234
|
跳转语句
break
跳出最近的循环或 switch
语句。
1
2
3
4
5
6
7
8
| for (int i = 1; i <= 100; i++)
{
if (i == 5)
{
break;
}
Console.WriteLine(i);
}
|
continue
马上下一次迭代。
1
2
3
4
5
6
7
8
| for (int i = 1; i <= 10; i++)
{
if (i < 9)
{
continue;
}
Console.WriteLine(i);
}
|
goto
语句跳转到一个标签:
return
终止方法执行,如果在 try
块里,会执行 finally
块 。
异常语句
throw
抛出异常,终止当前程序执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| using System;
namespace Throw2
{
public class NumberGenerator
{
int[] numbers = { 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 };
public int GetNumber(int index)
{
if (index < 0 || index >= numbers.Length)
{
throw new IndexOutOfRangeException();
}
return numbers[index];
}
}
|
throw
可以作为表达式
1
2
| string arg = args.Length >= 1 ? args[0] :
throw new ArgumentException("You must supply an argument");
|
异常可以通过 throw
重新抛出
1
2
3
4
5
6
7
8
| try
{
return Value[0];
}
catch (NullReferenceException e)
{
throw;
}
|
try-catch-finally
捕获异常
1
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
| public class EHClass
{
void ReadFile(int index)
{
// To run this code, substitute a valid path from your local machine
string path = @"c:\users\public\test.txt";
System.IO.StreamReader file = new System.IO.StreamReader(path);
char[] buffer = new char[10];
try
{
file.ReadBlock(buffer, index, buffer.Length);
}
catch (System.IO.IOException e)
{
Console.WriteLine("Error reading from {0}. Message = {1}", path, e.Message);
}
finally
{
if (file != null)
{
file.Close();
}
}
// Do something with buffer...
}
}
|
其它知识
region 和 endregion
region 和 endregion 表示一块区域,在 VS 可以折叠起来,方便查看。