C# base

C艹艹,忘光光。
好像学Unity的时候,直接当作语法略有不同的C艹来写了。应该没问题吧。
先写点基础。

概述

见过的C#用在

  • Unity游戏编程
  • 服务器后端

数据类型

C#是语言,.NET是运行时平台。首先我想先遍历一下基本的数据类型,后面再好好了解背后的编译原理吧。

$
\begin{cases}
\text{值类型 (Value Types)} &
\begin{cases}
\text{预定义数值类型} &
\begin{cases}
\text{整型: sbyte, byte, short, ushort, int, uint, long, ulong} \\
\text{浮点型: float, double, decimal}
\end{cases} \\
\text{预定义非数值类型: bool, char} \\
\text{用户自定义: struct, enum}
\end{cases} \\\\
\text{引用类型 (Reference Types)} &
\begin{cases}
\text{预定义类型: object, string, dynamic} \\
\text{用户自定义: class, interface, delegate, Array}
\end{cases}
\end{cases}
$

值类型

整型

byteshortintlong分别为1、2、4、8个字节长度。
其中表示有无符号的前缀,只有byte有个sbyte(即signed有符号的意思),其它的u起头,代表无符号

C# 类型/关键字 范围 尺寸 .NET类型
sbyte -128 到 127 8 位带符号整数 System.SByte
byte 0 到 255 无符号 8 位整数 System.Byte
short -32,768 到 32,767 带符号 16 位整数 System.Int16
ushort 0 到 65,535 无符号 16 位整数 System.UInt16
int -2,147,483,648 到 2,147,483,647 带符号的 32 位整数 System.Int32
uint 0 到 4,294,967,295 无符号 32 位整数 System.UInt32
long -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807 64 位带符号整数 System.Int64
ulong 0 到 18,446,744,073,709,551,615 无符号 64 位整数 System.UInt64
nint 取决于平台(在运行时计算) 带符号 32 位或 64 位整数 System.IntPtr
nuint 取决于平台(在运行时计算) 无符号 32 位或 64 位整数 System.UIntPtr

比起C++,C#的整型大小固定、溢出可控、命名统一。

每个整型的默认值为零。 0

每个整型类型都有MinValueMaxValue属性,用于提供该类型的最小值和最大值。

整数字面量

十进制、十六进制、二进制表示法:

1
2
3
var decimalLiteral = 42;
var hexLiteral = 0x2A;
var binaryLiteral = 0b_0010_1010;

浮点型

C# 类型/关键字 近似范围 精准率 尺寸 .NET 类型
float ±1.5 x 10~45 到 ±3.4 x 1038 ~6-9 位数字 4 个字节 System.Single
double ±5.0 × 10~324 到 ±1.7 × 10308 ~15-17 位数字 8 个字节 System.Double
decimal ±1.0 x 10-28 到 ±7.9228 x 1028 28-29 位数字 16 字节 System.Decimal

布尔型

类型 范围 尺寸 .NET 类型
bool truefalse 1 字节 System.Boolean

字符型

类型 范围 尺寸 .NET 类型
char U+0000 到 U+FFFF 16 位 System.Char

字符的定义方法可以是字符、unicode转义、十六进制转义

1
2
3
4
5
6
7
8
var chars = new[]
{
'j',
'\u006A',
'\x006A',
(char)106,
};
Console.WriteLine(string.Join(" ", chars)); // output: j j j j

[!IMPORTANT]

  • Unicode 转义序列中,必须指定4个十六进制数字。 也就是说, \u006A 是有效的转义序列,而 \u06A\u6A 无效。
  • 在十六进制转义序列中,可以省略前导零。

枚举

枚举都长差不多。默认int

1
2
3
4
5
6
7
enum Season
{
Spring,
Summer,
Autumn,
Winter
}

不按默认来:

1
2
3
4
5
6
7
enum ErrorCode : ushort
{
None = 0,
Unknown = 1,
ConnectionLost = 100,
OutlierReading = 200
}

总之差不多。

结构

struct,当C++用即可,后面学一下readonly

ref

元组

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.

元组长度可以任意。可以用var定义。

可空类型

让值类型也可以null。做游戏时最爱用的一集(读入null中)。用法是在类型后面加一个?

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];

不加问号不行

1
int a = null; // 编译器:打咩哟!介是啥呀?

内置类型转换

  • 任何整型数值类型都隐式转换为任何浮点数值类型

引用类型

字符串

字符串的常用成员

成员 类型 意义
Length 属性 返回字符串的长度
Concat 静态方法 返回连接多个参数字符串后的新字符串
Contains 方法 返回子串是否出现在当前字符串
Format 静态方法 返回根据格式字符串和参数格式化后的字符串
Insert 方法 在指定位置插入指定字符串,返回新字符串
Remove 方法 从当前字符串中移除指定范围的字符,返回新字符串
Replace 方法 将当前字符串中的所有指定字符或子串替换为另一字符或子串,返回新字符串
Split 方法 根据指定分隔符将,字符串分割为子字符串数组
Substring 方法 提取指定起始位置和长度的子字符串
ToLower 方法 返回小写副本
ToUpper 方法 返回大写副本

字符串解析为数据值

Parse方法。

1
2
string si = "25.873"
double dl = double.Parse(sl);

无法转换会抛出异常。

防止抛出异常,可以用TryParse

1
2
3
string s = "28";
int i;
bool success = int.TryParse(s, out i);

数组

System.Array

数组的声明

1
2
3
4
5
6
7
type[] arrayName;//数组的声明
type?[] arrayName; // 只有数组的元素可以null
type[]? arrayName; // 只有数组可以null
type?[]? arrayName; // 以上都可以null
//也许问号是右结合的?
int[] numbers = new int[10]; //默认全都为0
string[] messages = new string[10]; // 默认全都为null

矩形数组与交错数组

与C/C++不同,C#数组可以长度不一,称为“交错数组”

1
2
3
4
5
6
7
8
// 矩形数组
int[,] rect = new int[2, 3] { {1,2,3}, {4,5,6} }; //[,]是“秩说明符”,表示二维矩形数组。

// 交错数组
int[][] jagged = new int[3][];
jagged[0] = new int[] {1,2};
jagged[1] = new int[] {3,4,5};
jagged[2] = new int[] {6};

与其它语言比较,Python的“交错数组”就是嵌套列表(日常自由),Java本质上都是交错数组

1
2
3
4
5
6
7
8
9
// Java
// “矩形数组”
int[][] rect = new int[2][3];

// “交错数组”
int[][] jagged = new int[3][];
jagged[0] = new int[] {1,2};
jagged[1] = new int[] {3,4,5};
jagged[2] = new int[] {6};

数组的读写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// C#的读写
for (int i = 0; i < rect.GetLength(0); i++) // 行数
{
for (int j = 0; j < rect.GetLength(1); j++) // 列数
{
rect[i,j]=i*j;
Console.Write(rect[i, j] + " ");
}
Console.WriteLine();
}

for (int i = 0; i < jagged.Length; i++) // 行数
{
for (int j = 0; j < jagged[i].Length; j++) // 当前行的列数
{
jagged[i][j]=i*j;
Console.Write(jagged[i][j] + " ");
}
Console.WriteLine();
}

矩形数组有点像Python的这个

1
2
3
4
5
# Python
import numpy as np
arr = np.array([[1,2,3],
[4,5,6]])
print(arr[1, 2]) # 6

注意交错数组不能在声明语句中初始化顶层数组之外的数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// ❌错的
int[][][] jagArr = new int[3][4][];

// ❌错的
int[][][] jagArr = new int[][][];

// ✔对的
int[][][] jagArr = new int[2][][];
// ✔对的
int[][][] jagArr = new int[][][]
{
new int[][] { new int[]{1} },
new int[][] { new int[]{2,3} }
};

数组的常用方法

集合

详见后面“C#的面向对象”

接口

记录

record修饰符
(遇到了再学吧。)
看这里:https://www.cnblogs.com/shanfeng1000/p/14973804.html

1
public record Person(string FirstName, string LastName);
1
2
3
4
5
public record Person
{
public required string FirstName { get; init; }
public required string LastName { get; init; }
};

void

非托管类型

默认值

默认值表

类型 默认值
引用类型 null
内置整数数值类型 0
内置浮点型数值类型 0
bool false
char '\0'
enum 0 强制转换成该枚举类型(即使枚举中没有值为 0 的成员)
struct 将所有值类型字段设置为其默认值,将所有引用类型字段设置为null,生成的值。
可空值类型(T? null(即 HasValue = false,没有有效值)

默认值表达式

1
2
int a = default;
int a = default(int);

foreach

这是一个经典的foreach

1
2
3
4
5
int[] myArr = new int[]{5, 6, 7};
foreach (int x in myArr) //int可以改成var,自动推断类型。
{
Console.WriteLine($"{ x }");
}

foreach遍历时,只可读不可写。而且中途不能修改被遍历的可迭代对象(这里是myArr

异步foreach

解构foreach

C#的面向对象

访问修饰符

6个 类成员的访问修饰符

修饰符 含义 类似 C++ 类似 Java
public 都能访问 public public
private 仅在同一类/结构体内可访问 private private
protected 同一类+派生类可访问 protected protected
internal 同一程序集(项目)内可访问 无直接对应(C++ 用internal链接属性) default(包级私有)
protected internal 同一程序集​或​派生类(跨程序集也行) 无直接对应 无直接对应
private protected 同一程序集内的派生类​可访问(C# 7.2+)

2个 顶级类的访问修饰符

1
2
class MyInternalClass { }  // 默认 internal,仅同一程序集内可见
public class MyPublicClass { } //任何程序集都能引用

顶级类下的嵌套类可以用6个类成员的访问修饰符

接口

命名空间

异常

try-catch

异步编程

吐槽

微软你个入机。。。
入机翻译

不过我写的也很入机就是了。

一个编程语言都没有深入学。

想横向学习各个编程语言的编译原理(都,编译了吗?)