.NET基础
第一单元:环境配置
突然发现自己基础实在是太差了,有很多基础概念都不太理解,学习一天不是在找资料就是在查资料的路上。表达能力也不够,所以用这个文章记录一下学习.NET的心路历程。吸取之前学习的教训,争取快点过一遍基础内容之后再完善。先做再说!
01-C# .NET是什么
C#是现代的,面向对象的类型安全型的语言,由微软开发在.NET平台上运行。
C#和.NET的关系,就像.NET是一个舞台,c#就是在这个舞台上表演一样。其概括特性如下:
| 特点 | 简要说明 |
|---|---|
| 面向对象 | 用类、对象、继承、多态等组织代码,适合中大型项目 |
| 类型安全 | 编译期会做类型检查,减少很多运行时错误 |
| 现代语法 | 持续迭代:顶级语句、模式匹配、记录类型、异步等(C# 12~14) |
| 强类型 | 变量必须有明确类型(如 int、string),便于维护和工具支持 |
.NET是免费,跨平台的开发和运行平台,c#代码会在.net上运行
其概括特点如下:
| 概念 | 说明 |
|---|---|
| .NET SDK | 开发工具包,包含编译器、运行时、模板等,用来写和运行 .NET 程序 |
| .NET 运行时 | 负责在电脑上“跑”已编译的 .NET 程序,代码交予运行时运行 |
| .NET 生态 | 除 C# 外还有 F#、VB.NET;以及 ASP.NET Core、.NET MAUI、EF Core 等框架 |
02-安装与编译
安装有sdk和运行时,这里重复提及之前的内容
SDK是什么
.NET SDK 是用于开发 .NET 应用的工具包,通常包含:
- C# 等语言的编译器及相关工具
- .NET 运行时(Runtime)
- 项目模板与命令行工具(
dotnet命令)
SDK 与运行时的基本区别
| 组件 | 作用 | 典型场景 |
|---|---|---|
| .NET 运行时 | 仅运行已经编译好的 .NET 程序 | 最终用户只需要运行应用 |
| .NET SDK | 包含运行时 + 编译器 + 工具 + 模板 | 开发者需要编译、运行和调试代码 |
也就是说,开发的时候需要下载.NET SDK。如果仅仅是运行他人编译好的.NET代码,使用运行时即可。
安装完成之后可以输入代码dotnet --version来判断当前设备是否安装SDK
dotnet --version输入之后会显示对应的版本信息:

此外使用dotnet --info 命令,可以查看更加详细的内容。它的主要作用是输出当前计算机上 .NET 环境的详细配置信息
dotnet --info
如果不清楚某些.NET命令,可以使用dotnet --help命令进行查看:
dotnet --help
03-使用VS编写代码
什么是Visual Studio?
VS是微软官方提供的集成开发环境,专门为.NET和C#开发设计,其功能特性如下:
| 功能 | 说明 |
|---|---|
| 代码编辑器 | 语法高亮、智能提示、代码补全、重构工具 |
| 调试器 | 设置断点、逐语句执行、查看变量值、调用栈分析 |
| 项目管理 | 创建项目、管理依赖、编译、运行、发布 |
| 图形界面 | 可视化操作,无需记忆命令行参数 |
| 集成工具 | NuGet 包管理、Git 版本控制、测试工具等 |
有两种开发方式:
| 开发方式 | 特点 | 适用场景 |
|---|---|---|
| Visual Studio | 图形界面、功能丰富、适合大型项目 | 日常开发、调试、学习阶段 |
| dotnet CLI | 命令行、轻量、适合自动化脚本和 CI/CD | 快速创建项目、服务器部署、脚本化 |
工作负荷
这是我第一次接触这个概念,简单来说就是在当前设备可以下载的依赖
在内容界面中,可以选取需要的开发功能进行下载

点开这个选项之后,就会运行到install中,内部有非常多的工作负荷供我们下载

项目结构
创建一个控制台项目,主要界面如下:

我们可以点击具体的项目文件,查看当前程序的属性。也可以右击查看当前项目的项目文件

此外也可以对当前项目更换目标框架以及其它重要信息,具体来说是更换当前项目的属性信息

运行
在VS界面中,按F5既可以运行这个项目

04-使用顶级语句输出代码
这里需要输出hello world,感觉有点蠢但是必须要做。
顶级语句
顶级语句是c#9.0引入的概念,允许在文件顶层直接编写可执行代码,无需定义namespace,class 以及Main方法。
| 特性 | 说明 |
|---|---|
| 简洁 | 代码更简洁,减少样板代码 |
| 易学 | 初学者更容易理解,无需先学类和方法 |
| 现代 | 符合现代 C# 的编程风格 |
顶级语句会在编译的时候将其转化为完整结构,例如一个console.writeline("hello world")会自动转为如下的结构:
using System;
namespace HelloWorld{ class Program { static void Main(string[] args) { Console.WriteLine("Hello, World!"); } }}使用编译器运行
使用顶级语句输出Hello World很简单,在Program.cs中填写下面语句即可:
Console.WriteLine("Hello, World!");Console.ReadKey(); // 暂停这个位置代码解析:
-
Console:.NET 提供的类,用于控制台输入输出操作
-
WriteLine:方法,用于向控制台输出一行文本,并在结尾添加换行符
-
“Hello, World!”:字符串字面量,要输出的文本内容
-
;:语句结束符,表示语句结束

使用命令行创建
在CMD中使用dotnet命令进行编写程序,会自动在当前目录下创建一个.NET控制台文件(因为是console)

创建完成之后会出现一个项目文件夹,文件夹中有具体的项目信息:

使用命令行运行,需要进入到项目目录中

输入命令donet run,就可以对当前项目进行编译运行
donet run
与传统语法的区别
| 写法 | 代码量 | 学习难度 | 适用场景 |
|---|---|---|---|
| 顶级语句 | 1 行 | 低 | 简单程序、学习阶段 |
| 传统写法 | 11 行 | 中 | 复杂程序、需要显式结构 |
05-基于文件/项目的应用
基于文件的应用编写方式,就是创建一个.cs文件,然后在cmd中使用具体的运行代码。
基于项目的应用编写方式,就是使用VS来进行运行程序
| 特性 | 基于文件的应用 | 基于项目的应用 |
|---|---|---|
| 文件结构 | 单个 .cs 文件 | 包含 .csproj 文件和多个 .cs 文件 |
| 创建方式 | 直接创建 .cs 文件 | 使用 dotnet new 或 Visual Studio 创建项目 |
| 运行方式 | dotnet run Program.cs | dotnet run(在项目目录中) |
| 适用场景 | 快速测试、简单脚本、学习语法 | 复杂应用、多文件项目、生产环境 |
| 版本要求 | C# 14 / .NET 10+ | 所有版本支持 |
一般来说,基于文件的应用优势:
-
快速启动:无需创建项目文件,直接编写代码即可运行
-
简单易学:适合初学者快速测试代码片段
-
轻量级:不需要
.csproj文件,文件结构更简单 -
适合脚本:可以像脚本一样快速执行 C# 代码
基于项目的应用的优势:
- 完整功能:支持 NuGet 包管理、多文件组织、复杂配置
- 生产就绪:适合实际项目开发
- 工具支持:Visual Studio、Rider 等 IDE 提供完整支持
- 版本控制:项目文件可以记录依赖和配置信息
基于文件的运行
使用dotnet run指定C#文件,运行

单文件运行是工具,一般用于测试小型样例或者新出现的语法

练习1:使用dotnet run 运行一个程序 输出姓名与年龄
using System;
var name = "张山";var age = 18;
Console.WriteLine(name);Console.WriteLine(age);
练习2:输出一个方程
using System;
int a = 15;int b = 27;int sum = a + b;Console.WriteLine($"{a} + {b} = {sum}");
06-传统写法
传统写法的结构:
using System;
namespace HelloWorld{ class Program { static void Main(string[] args) { Console.WriteLine("Hello, World!"); } }}对于这些结构做简要说明:
- using指令:引入命名空间
- namespace声明:定义命名空间
- class声明:定义类,通常为Program
- main方法:程序的入口
命名空间
namespace 规定一个空间,这里假设为A空间。任何没有引用A空间的程序,无法使用A空间中的代码内容。
命名空间的目的是为了防止不同空间的相同函数名称起冲突。引入命名空间的方法有两种
- 使用Using指令引入
using System;
namespace HelloWorld{ class Program { static void Main(string[] args) { Console.WriteLine("Hello, World!"); // 方法在system命名空间中 } }}- 使用完全限定名称引入
namespace HelloWorld{ class Program { static void Main(string[] args) { System.Console.WriteLine("Hello, World!"); } }}类
在传统写法中,代码都必须放置在类中
using System;
namespace HelloWorld{ // 可运行代码都必须放在类中 class Program { // 类的成员放在这里 }}- 封装代码:组织相关代码
- 定义类型:类是C#代码的基本类型之一
- 程序入口:Main方法一般在Program类中
Main方法
Main 方法是 C# 程序的入口点 (entry point):
- 程序运行时,首先执行
Main方法
-
每个可执行程序必须有且仅有一个
Main方法 -
Main方法必须是static(静态)的
Main的常见签名:
class Program { static void Main():无参数
static void Main(string[] args):带参数(接收命令行参数)
static int Main():返回 int(退出码)
static int Main(string[] args):带参数并返回退出码 }Main的要求:
- 必须是
static(静态方法) - 返回类型是
void或者int - 方法名称必须是
Main(区分大小写) - 参数(如果有)必须是string[]类型,参数名称通常是args
07-编译与运行
编译一个程序的目的是让这个代码从高级语言转化为机器能读懂的低级语言
在C#中使用dotnet build来进行编译项目
dotnet build在项目所在目录(包含 .csproj 的目录)下执行,会读取当前目录下的 .csproj,编译该项目下的所有 .cs 文件。

在cmd中输入上述命令

执行完成之后会生成一个bin目录,会在里面生成可执行目录和依赖,其中的exe是可以运行的,运行结果与dotnet run一致。

dotnet build 与 dotnet run的区别
| 命令 | 作用 |
|---|---|
dotnet build | 只编译,不运行;输出在 bin/obj |
dotnet run | 按需编译 + 运行;开发时最常用 |
典型项目结构
一个经典的c#项目结构如下:
MyApp/├── MyApp.csproj # 项目文件├── Program.cs # 程序入口代码├── bin/ # 编译输出(通常不提交版本控制)└── obj/ # 中间产物(通常不提交版本控制).csproj 定义目标框架、输出类型、依赖等;同一目录下的 .cs 默认属于该项目。
.csproj内容
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net10.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup></Project>OutputType: Exe 表示可执行程序;TargetFramework: 目标框架,如 net10.0。
使用命令行创建控制台项目
在希望创建项目的父目录执行:dotnet new console -n MyApp

其创建的项目目录如下:

当然也可以不使用-n 名字,执行这个dotnet new console命令会自动以当前的文件夹名称创建项目目录程序:

bin 与 obj:版本控制建议
bin:最终编译结果(可执行文件、DLL)
obj:中间编译结果,供增量编译使用
建议将 bin/ 和 obj/ 加入 .gitignore,不提交到 Git。这些目录可由 dotnet build 重新生成。
第二单元:语法和结构
08-注释
c#中一般使用单行或者多行注释
// 这是单行注释
//* 这是多行注释*//一般来说使用注释需要注意以下要点:
| 用途 | 说明 |
|---|---|
| 说明意图 | 解释“为什么”这样写,而不是重复“做什么” |
| 暂时禁用代码 | 调试时用 // 或 /* */ 包住代码 |
| TODO / FIXME | 标记待完成或待修复 |
| 模块/方法说明 | 在文件头或方法上方写简短说明 |
在后天需要养成的书写习惯与原则
-
写“为什么”而不是“做什么”:
i++;不必注释“i 加 1”,可注释“循环计数,用于控制输出行数” -
避免过度注释:代码能读懂的不必每行都注释;注释应与代码同步更新
-
保持简洁:注释尽量简短清晰;过长说明可放在文档中
09-语句与代码块
语句(statement)是程序执行的基本单位,程序按顺序执行一条条语句。
-
声明语句:如
int a = 10; -
表达式语句:如
Console.WriteLine("Hi");、a++; -
控制流语句:如
if、for、return
多数语句以分号 ; 结束。
分号的作用
一个分号的出现标识着这个语句的结束,常用的有如下:
-
变量声明与赋值:
int a = 10; -
方法调用作为语句:
Console.WriteLine("Hello"); -
赋值、自增等:
x = 5;、i++;
int x = 5;console.writeline(x);console.ReadKey();代码块
代码块就是使用{}括起来的一条或者多条语句,一般用法如下:
-
方法体:如
static void Main() { ... } -
控制流:
if、for等后面的多条语句放在一个块里 -
嵌套块:块内可以再写块
{ 语句1; 语句2;}语句与表达式
在执行内容上,表达式用于“计算值”,而语句用于“执行操作”。
-
表达式 (expression):会计算出一个值,如
3 + 5、a * 2、Console.ReadLine() -
语句 (statement):执行操作的完整单位,如
int a = 10;、Console.WriteLine("Hi"); -
很多语句里会包含表达式(如
a = 3 + 5;中的3 + 5) -
仅执行表达式并丢弃结果时,称为表达式语句,同样以分号结束
10.Using与命名空间
命名空间的作用是将不同的类和结构组合在一起,不同的命名空间内部的同名类型互不冲突
// 两个命名空间的相同名称方法互不冲突namespace A{ a(); b()}
namespace B{ a(); b();}在顶部使用using指令,表示本文件中可以使用system下的类型名称,不用写当前命名空间的前缀
using System;
Console.WriteLine("Hello");string s = Console.Readline();命名空间 与 函数 的关系
这里以System 和 Console举例,Console是位于System中的一个类
因此:完整的类型名为System.console,引入了Using system后,可只写Console
using System;
Console.WriteLine("Hello, World!");Console.Write("请输入: ");string input = Console.ReadLine();如果不引入System,这种方式叫做完全限定名称,代码如下:
System.Console.WriteLine("Hello, World!");System.Console.Write("请输入: ");string input = System.Console.ReadLine();声明与使用命名空间
可以在代码中自己创建命名空间。
“声明 namespace” 和 “using 引用命名空间” 是两件事:前者是 “我的代码放在哪个命名空间”,后者是 “我要使用哪些命名空间里的类型”。
11-标识符与命名规范
标识符是程序员为变量、类、方法、命名空间等起的名字,如 Program、userName。
-
由字母、数字、下划线
_组成 -
首字符不能是数字
-
不能与 C# 关键字完全相同(除非用
@前缀)
合法:name、userName、_count、MyClass。
不合法:2nd(数字开头)、class(关键字)。C# 是大小写敏感的,UserName 与 username 是不同标识符。
标识符书写方法
PascalCase(帕斯卡),每个单词首字母大写,如 Program、WriteLine、MyApp。用于:类名、方法名、命名空间、属性、常量等。
camelCase(驼峰),首单词小写,后续单词首字母大写,如 userName、countOfItems。用于:局部变量、方法参数、私有字段等。
| 用途 | 推荐风格 | 示例 |
|---|---|---|
| 类、方法、命名空间 | PascalCase | Program、WriteLine |
| 局部变量、参数 | camelCase | userName、count |
关键字
关键字是 C# 预留的、有特殊含义的单词,不能作为普通标识符使用。
常见关键字举例
-
类型:
int、string、bool、void、class -
流程控制:
if、else、for、while、return -
修饰与结构:
public、static、namespace、using
例如 int class = 10;、string if = "hi"; 会报错,应改用合法标识符(如 className、condition)。
@前缀
在特殊情况下(如对接 API 要求属性名为 class),可在标识符前加 @,使关键字被当作普通标识符使用,例如 @class、@int。
建议:仅在有充分理由时使用;一般优先改用语义相近的合法名称(如 classId、returnValue),可读性更好。
命名习惯
一般而言需要注意的命名习惯:
-
见名知意:名称应能反映用途,避免无意义的
a、b(循环变量i、j除外) -
与规范、团队一致:遵循 PascalCase/camelCase 常规用法,团队有约定则保持一致
-
避免易混:不要仅靠大小写区分两个标识符(如同时存在
user与User),避免过长或过短极端
第三单元:类型与变量
12-值类型与引用类型
在编程语言中,一切数据都有类型。
类型决定了一个数据应该使用多少内存,如何存储。能进行那些操作以及与其它类型之间的转换关系。
例如:42 是 int 类型,"Hello" 是 string 类型,true 是 bool 类型。
声明变量时要指定类型(或使用 var 推断);方法参数和返回值也有类型。建立类型系统的整体认识是学习变量、方法、类的基础。
值类型
值类型的特点如下:
-
存储:数据直接存放在变量所属的内存位置
-
复制/赋值:赋值的时候赋值一份值,两个变量各自持有一份数据。修改其中一个数据不会影响到另一个数据
-
常见值类型:int、double、bool、char、decimal、枚举、结构体 (struct) 等。
int a = 10;int b = a; // b 得到的是10的副本a = 20; // 只改 a. b 仍是 10引用类型
-
存储: 实际数据在堆上;变量中保存的是引用(可理解为“指向对象的地址”)。
-
赋值/复制: 赋值时复制的是引用,两个变量可能指向同一块数据。通过其中一个修改,另一个访问到的也会变化。
-
常见引用类型: string、类 (class)、数组、接口等。
int[] arr1 = new int[] { 1, 2, 3 };int[] arr2 = arr1; // arr2 和 arr1 指向同一数组arr1[0] = 99;string类型是引用类型,修改会得到新的字符串,不会修改原内容
二者对比
| 维度 | 值类型 | 引用类型 |
|---|---|---|
| 存储 | 数据在变量所在处 | 数据在堆,变量存引用 |
| 赋值语义 | 复制值 | 复制引用(可能共享对象) |
| 常见例子 | int、bool、struct | string、class、数组 |
问题,什么是堆?
13-变量声明与初始化
声明一个变量的语法如下:
int count = 0;string name = "Tom";double price = 9.99;先声明、后赋值也可以:int count; 然后 count = 0;。变量名需符合标识符规则,局部变量建议用 camelCase。
局部变量
C# 规定:局部变量在使用前必须被明确赋值,否则编译器报错。
int x;Console.WriteLine(x); // 错误:x 未赋值先赋值再使用则合法:x = 10; 之后 Console.WriteLine(x); 正确。
var与类型推断
var 表示 “由编译器根据右侧初始化表达式推断类型”。推断完成后,变量类型是确定的,与手写类型等价。
var count = 10; // 推断为 intvar name = "Tom"; // 推断为 stringvar price = 9.99; // 推断为 double限制:声明时必须同时初始化;仅适用于局部变量。不能写 var x; 再单独赋值。
何时使用
- 适合用
var: 右侧类型较长或重复(如泛型)、类型显而易见(如var name = "Tom";),可提高可读性 - 适合用显式类型: 希望强调类型、或右侧是接口/基类而希望变量类型更明确时
14-基础类型
类型名:
int
字面量:直接写数字,如 0、42、-100,不加引号、不带小数点。
int count = 0;int age = 18;注意:带小数点的数(如 3.14)视为 double,不能直接赋给 int;需类型转换时
类型名:
double
面量:带小数点的数,如 3.14、0.5、-2.5。
double price = 9.99;double rate = 0.85;与 int 的区分:有小数点即为浮点(默认 double),无小数点即为整数(默认 int)。
类型名:
bool
类型名:bool。字面量:仅有两个值—— true 与 false。
bool isOk = true;bool flag = false;条件判断(if)与循环(for、while)中的条件表达式结果为 bool 类型。
类型名:
char
字面量:用单引号包围的一个字符,如 'A'、'1'、'中'。
与 string 的区分(易混点)
| 类型 | 引号 | 内容 | 示例 |
|---|---|---|---|
| char | 单引号 | 恰好一个字符 | 'A' |
| string | 双引号 | 零个或多个字符 | ""、"A"、"Hello" |
注意:"A" 是 string,不是 char;将 "A" 赋给 char 变量会导致类型不匹配。
char grade = 'A';char digit = '9';15-c#字符串
类型名:
string。
字面量:用双引号包围的文本,如 "Hello"、"世界";空字符串为 ""。
与 char 的区分:
char为单引号、单字符;string为双引号、可零个或多个字符,故"A"为string。
字符串拼接
在涉及字符串的语境下,+ 表示拼接,而非数学加法。可将多个字符串连接;也可将字符串与数字等连接,此时数字会先转为字符串再拼接。
string a = "Hello";string b = "World";string c = a + ", " + b; // "Hello, World"string msg = "年龄: " + 18; // "年龄: 18"注意:两侧均为数字时 + 为数学加法;只要有一侧为 string,+ 即为拼接。
Length 属性
Length 是 string 的属性,表示字符串中字符的个数(字符数,非字节数)。该属性为只读,不可赋值修改。
string s = "Hello";int len = s.Length; // 5空字符串 "" 的 Length 为 0。后续会常用 Length 做判空或循环边界。
16-字符串方法
string 不可变
在 C# 中,string 是不可变的:一旦字符串对象创建,其内容不可被修改。调用 Trim、Replace 等方法时,不会修改原字符串,而是返回一个新的字符串。
若需使用结果,应赋给变量(如 s = s.Trim();)或链式调用(如 s.Trim().Replace("a", "b"));原串本身不变。
- 注意:仅写
s.Trim();而不接收返回值,则s仍带首尾空白。string的方法大多返回新串,要结果须接住返回值。
Trim 与 Replace
Trim:去除字符串首尾空白,中间空白不处理;返回新字符串。 例:" Hello ".Trim() -> "Hello"。
// Tirm 是 去除首尾空格string A = " 15678315 313456313 13413132 654465132063 646513216546 3465 ";A = A.Trim();Console.WriteLine(A);
Replace(旧子串, 新子串):将当前字符串中所有出现的「旧子串」替换为「新子串」,返回新字符串。
string B = " Hello World ";var C = B.Replace("Hello", "c#"); // B是无法修改的,这里输出B还是原来的Console.WriteLine(C);
Contains、StartsWith、EndsWith
三者均返回 bool(true/false),常用于条件判断(如 if、循环条件):
| 方法 | 含义 |
|---|---|
| Contains(子串) | 当前字符串是否包含该子串 |
| StartsWith(子串) | 是否以该子串开头 |
| EndsWith(子串) | 是否以该子串结尾 |
string s = "Hello World";s.Contains("World"); // trues.StartsWith("Hello"); // trues.EndsWith("ld"); // true17-字符串插值与多变量输出
字符串插值语法
在字符串字面量前加 $, 在字符串内用 { 变量或表达式 } 嵌入值。大括号内的内容会被求值并转为字符串,再与前后文本拼接。
string name = "Tom";int age = 18;string msg = $"姓名: {name}, 年龄: {age}"; // "姓名: Tom, 年龄: 18"注意:$ 须紧挨双引号;大括号内写变量或表达式,不写语句。
大括号内可以是表达式
{ } 内不仅可以写变量名,也可以写表达式(如运算、方法调用);表达式会先求值再转为字符串嵌入。
int a = 10, b = 20;string s = $"{a} + {b} = {a + b}"; // "10 + 20 = 30"
与 + 拼接对比与 Console.WriteLine
| 方式 | 写法示例 | 特点 |
|---|---|---|
| 插值 | $"姓名: {name}, 年龄: {age}" | 结构清晰,多变量时更易读 |
| 拼接 | "姓名: " + name + ", 年龄: " + age | 等价,但加号多、易漏写 |
多变量或表达式较多时,推荐使用字符串插值。常见用法:Console.WriteLine($"姓名: {name}, 年龄: {age}");
18-常量与运算顺序
常量
字面量是指在代码中直接写出的值,未通过变量或常量名引用的写法。字面量有类型;赋给变量或常量后,变量/常量持有该值。
常见字面量举例:
- 整数:
0、42、-100 - 浮点:
3.14、0.5 - 布尔:
true、false - 字符:
'A';字符串:"Hello"
语法: const 类型 常量名 = 值;。声明时必须赋值;赋值后不可再修改。
const int MaxCount = 100;const double Pi = 3.14159;const string AppName = "MyApp";常量适合表示固定不变的值,便于阅读与维护。与变量的区别:变量可多次赋值,常量只能赋一次。
运算顺序
在表达式中,乘、除的优先级高于加、减;括号可以改变顺序,括号内的先算。
-
2 + 3 * 4:先算3 * 4得 12,再2 + 12得 14 -
(2 + 3) * 4:先算2 + 3得 5,再5 * 4得 20
19-算术运算符
算术运算符用于执行基本的数学运算,是编程中最常用的运算符类型。
| 运算符 | 名称 | 示例 | 结果 |
|---|---|---|---|
| + | 加法 | 5 + 3 | 8 |
| - | 减法 | 10 - 4 | 6 |
| * | 乘法 | 3 * 4 | 12 |
| / | 除法 | 15 / 3 | 5 |
| % | 取余/模 | 10 % 3 | 1 |
加法运算符(+)
基础用法:
int a = 5;int b = 3;int sum = a + b; // sum = 8
Console.WriteLine(sum); // 输出: 8字符串拼接
string firstName = "张";string lastName = "三";string fullName = firstName + lastName; // "张三"当+的一侧是字符串,另一侧会被转化为字符串
int age = 25;string message = "我今年" + age + "岁"; // "我今年25岁"减法运算符(-)
基本用法
int a = 10;int b = 4;int difference = a - b; // difference = 6
Console.WriteLine(difference); // 输出: 6负数表示
- 也可以作为一元运算符表示负数:
int negative = -5; // negative = -5乘法运算符(*)
基本用法
int a = 3;int b = 4;int product = a * b; // product = 12
Console.WriteLine(product); // 输出: 12实际应用示例
int price = 25; // 单价int quantity = 3; // 数量int total = price * quantity; // 总价 = 75除法运算符(/)
当两个操作数都是整数时,结果也是整数,且向下取整(截断小数部分)。
int a = 10;int b = 3;int result = a / b; // result = 3 (不是 3.333...)
Console.WriteLine(result); // 输出: 3注意: 10 / 3 在数学中是 3.333...,但在整数除法中结果是 3(小数部分被丢弃)。
如果至少有一个操作数是浮点数(double 或 float),结果就是浮点数:
double a = 10.0;double b = 3.0;double result = a / b; // result = 3.3333333333333335
Console.WriteLine(result); // 输出: 3.3333333333333335混合类型
int a = 10;double b = 3.0;double result = a / b; // result = 3.3333333333333335(自动提升为 double)取余运算符(%)
取余运算符 % 计算两个数相除后的余数。
int a = 10;int b = 3;int remainder = a % b; // remainder = 1 (10 ÷ 3 = 3 余 1)
Console.WriteLine(remainder); // 输出: 1应用方式:
- 判断奇偶性:
number % 2 == 0表示偶数
int number = 7; if (number % 2 == 0) { Console.WriteLine("偶数"); } else { Console.WriteLine("奇数"); // 输出: 奇数 }- 周期性计算: 计算星期几、循环索引等
int totalMinutes = 125; int hours = totalMinutes / 60; // hours = 2 int minutes = totalMinutes % 60; // minutes = 5- 提取数字的某一位:
number % 10得到个位数字
int number = 1234; int lastDigit = number % 10; // 个位: 4 int tensDigit = (number / 10) % 10; // 十位: 3优先级
从高到低:
- 括号
(): 最高优先级 - 乘、除、取余
\*、/、%: 同级,从左到右 - 加、减
+、-: 同级,从左到右
int result1 = 2 + 3 * 4; // 14(先算 3 * 4 = 12,再算 2 + 12 = 14)int result2 = (2 + 3) * 4; // 20(先算括号:2 + 3 = 5,再算 5 * 4 = 20)int result3 = 10 / 2 * 3; // 15(从 left 到 right:10 / 2 = 5,再算 5 * 3 = 15)int result4 = 10 % 3 + 5; // 6(先算 10 % 3 = 1,再算 1 + 5 = 6)20-整数除法与余数
当两个操作数都是整数时,结果也是整数,且向下取整(截断小数部分)。
int a = 10;int b = 3;int result = a / b; // result = 3 (不是 3.333...)整数除法不是四舍五入,而是直接截断(向零方向取整)
-
10 / 3 = 3(余数 1 被丢弃) -
-10 / 3 = -3(向零方向取整,不是 -4)
整数除法的数学含义
整数除法 a / b 的结果是:最大的整数 q,使得 q * b <= a.
int a = 17;int b = 5;int quotient = a / b; // quotient = 3 (17 ÷ 5 = 3 余 2)int remainder = a % b; // remainder = 2
// 验证: a = quotient * b + remainderConsole.WriteLine($"{a} = {quotient} * {b} + {remainder}"); // 17 = 3 * 5 + 2负数除法的行为:
int a = -10;int b = 3;int result = a / b; // result = -3 (向零方向取整)规则:整数除法的结果向零方向取整,而不是向下取整。
整数类型的取值范围
这是一张关于 “常用整数类型及其范围”(C# / .NET 常见数据类型) shore 的表格。以下是图片中包含的完整文本内容:
| 类型 (Type) | 大小 (字节/Bytes) | 最小值 (Min Value) | 最大值 (Max Value) |
|---|---|---|---|
| sbyte | 1 | -128 | 127 |
| byte | 1 | 0 | 255 |
| short | 2 | -32,768 | 32,767 |
| ushort | 2 | 0 | 65,535 |
| int | 4 | -2,147,483,648 | 2,147,483,647 |
| uint | 4 | 0 | 4,294,967,295 |
| long | 8 | -9,223,372,036,854,775,808 | 9,223,372,036,854,775,807 |
| ulong | 8 | 0 | 18,446,744,073,095,551,615 |
查看具体的取值范围
可以使用 MinValue 和 MaxValue 属性查看类型的取值范围:
Console.WriteLine($"int 最小值: {int.MinValue}"); // -2,147,483,648Console.WriteLine($"int 最大值: {int.MaxValue}"); // 2,147,483,647Console.WriteLine($"long 最小值: {long.MinValue}"); // -9,223,372,036,854,775,808Console.WriteLine($"long 最大值: {long.MaxValue}"); // 9,223,372,036,854,775,807字面量的类型推断
var a = 100; // int (默认)var b = 100L; // long (后缀 L)var c = 100U; // uint (后缀 U)var d = 100UL; // ulong (后缀 UL)整数溢出
当计算结果超出类型的取值范围时,会发生溢出(overflow)。
int max = int.MaxValue; // 2,147,483,647int result = max + 1; // 溢出!结果变成 -2,147,483,648 (最小值)注意:C# 默认情况下不会抛出异常,而是发生环绕(wrap around)。
int a = int.MaxValue;Console.WriteLine(a); // 2,147,483,647Console.WriteLine(a + 1); // -2,147,483,648 (溢出到最小值)检测溢出异常
可以使用 checked 关键字在溢出时抛出异常:
checked{ int max = int.MaxValue; int result = max + 1; // 抛出 OverflowException}或者使用 checked 表达式:
int max = int.MaxValue;int result = checked(max + 1); // 抛出 OverflowException如何选择合适的整数类型
选择原则:
- 默认使用
int:除非有特殊需求,否则使用int(最常用,性能好) - 需要更大范围时使用
long:当数值可能超过int的范围时 - 节省内存时使用
short或byte:在数组或集合中存储大量数据时 - 无符号类型:当确定数值不会为负数时,可以使用
uint、ulong等
byte age = 25; // 年龄:使用 byte 足够int priceInCents = 9999; // 价格(分):使用 int 足够long fileSize = 5_000_000_000L; // 文件大小:使用 long取余与整数除法的关系
int a = 17;int b = 5;
int quotient = a / b; // 商: 3int remainder = a % b; // 余数: 2
// 验证: a = quotient * b + remainderConsole.WriteLine($"{a} = {quotient} * {b} + {remainder}"); // 17 = 3 * 5 + 2取余结果的符号与被除数(左操作数)相同:
Console.WriteLine(10 % 3); // 1Console.WriteLine(-10 % 3); // -1Console.WriteLine(10 % -3); // 1Console.WriteLine(-10 % -3); // -1