一、什么是.NET平台
.NET 是由 Microsoft 支持的免费开放源代码应用程序平台。
.NET
- .NET 是一个安全、可靠且高性能的应用程序平台。
- C# 是 .NET 的编程语言。它是强类型且类型安全的,并集成了并发和自动内存管理。
C#
- C# 是一种新式、安全且面向对象的编程语言,既有面向数据的记录等高级功能,也有函数指针等低级功能。
跨平台
- Android、Apple、Linux 和 Windows 操作系统均支持 .NET。它在多个 Linux 发行版(如 Red Hat Enterprise Linux 和 Ubuntu)中提供。
库
- .NET 包括一组标准库和 API,涵盖集合、网络到机器学习。
- NuGet 是 .NET 的包管理器,包含超过 300000 个包。
应用
工具
- 可以在任何操作系统上使用 .NET CLI、Visual Studio、Visual Studio Code 和其他工具生成应用。配置与GitHub Actions 等服务的持续集成并将部署推送到云的操作非常简单。
二、搭建C#开发环境
安装SDK
https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/sdk-9.0.300-windows-x64-installer
安装Visual Studio2022
https://visualstudio.microsoft.com/zh-hans/vs/
三、C#基础
C#程序示例及程序结构解释
📄控制台应用程序
// 引入命名空间:System 包含基本类,如 Console、String 等
using System;
// 定义一个命名空间,用于组织代码,避免类名冲突
namespace MyFirstApp
{
// 定义一个类,C# 中所有代码必须写在类中
class Program
{
// 主函数:程序入口点
// args 表示命令行参数(可选)
static void Main(string[] args)
{
// 输出欢迎信息到控制台
Console.WriteLine("欢迎使用 C# 示例程序!");
// 调用自定义方法
GreetUser();
// 提示用户输入姓名
Console.Write("请输入你的名字:");
string name = Console.ReadLine(); // 读取用户输入
// 使用字符串插值输出问候语
Console.WriteLine($"你好, {name}!欢迎来到 C# 世界。");
// 显示程序结束提示
Console.WriteLine("程序即将结束,请按任意键退出...");
Console.ReadKey(); // 阻塞程序直到用户按键
}
// 自定义方法:向用户打招呼
static void GreetUser()
{
Console.WriteLine("很高兴见到你!");
}
}
}
🧩 模板结构说明
部分 | 说明 |
---|---|
using System; |
引入系统命名空间,提供常用类支持 |
namespace MyFirstApp |
将代码封装进一个逻辑组中,方便管理和维护 |
class Program |
定义一个类,包含程序逻辑 |
static void Main(string[] args) |
程序入口方法,程序从这里开始执行 |
Console.WriteLine() |
向控制台输出一行文本并换行 |
Console.Read() / ReadLine() |
从控制台读取用户输入 |
GreetUser() |
自定义静态方法,用于模块化代码 |
Console.ReadKey() |
防止控制台窗口一闪而过,等待用户按键 |
▶️ 运行效果(模拟)
欢迎使用 C# 示例程序!
很高兴见到你!
请输入你的名字:李四
你好, 李四!欢迎来到 C# 世界。
程序即将结束,请按任意键退出...
📌 小贴士
- 可以将这个模板保存为
.cs
文件(如Program.cs
),然后使用 Visual Studio 或 .NET CLI 编译运行。 - 若使用 .NET CLI:
dotnet new console -n MyFirstApp cd MyFirstApp dotnet run
C#控制台读取值
✅示例:读取姓名和年龄,并输出信息
using System;
class Program
{
static void Main()
{
// 读取姓名
Console.Write("请输入你的名字:");
string name = Console.ReadLine();
// 读取年龄并转换为整数
Console.Write("请输入你的年龄:");
int age = int.Parse(Console.ReadLine());
// 输出结果
Console.WriteLine($"你好,{name}!你今年 {age} 岁。");
// 等待按键退出
Console.WriteLine("按任意键退出...");
Console.ReadKey();
}
}
📝 注释说明(逐行解释)
代码 | 注释 |
---|---|
using System; |
引入系统命名空间,使用基础类库 |
class Program |
定义一个类,C# 所有代码都写在类中 |
static void Main() |
程序入口点 |
Console.Write(...); string name = ... |
提示并读取用户输入的名字 |
Console.Write(...); int age = ... |
提示并读取年龄,使用 int.Parse 转换为整数 |
Console.WriteLine(...) |
使用字符串插值输出信息 |
Console.ReadKey() |
防止控制台窗口关闭,等待用户按键 |
⚠️ 注意事项(补充)
int.Parse()
:如果输入不是合法整数,程序会崩溃。- 推荐使用
int.TryParse()
来进行安全转换(适用于更健壮的程序)。
C#命令行参数
🧩什么是命令行参数?
当你运行一个控制台程序时,可以在启动命令后面加上一些额外的信息(称为参数),例如:
MyApp.exe 参数1 参数2 参数3
这些参数可以通过 Main
方法的参数 string[] args
来获取和处理。
✅ 代码示例
using System;
class Program
{
static void Main(string[] args)
{
// 检查是否有参数传入
if (args.Length == 0)
{
Console.WriteLine("未提供任何命令行参数。");
return;
}
// 输出所有参数
Console.WriteLine("你输入的命令行参数为:");
for (int i = 0; i < args.Length; i++)
{
Console.WriteLine($"参数 {i}:{args[i]}");
}
}
}
📝 注释说明
行号 | 代码 | 注释 |
---|---|---|
1 | using System; |
引入系统命名空间,用于控制台输入输出 |
3 | class Program |
定义一个类 |
5 | static void Main(string[] args) |
程序入口,args 是命令行参数数组 |
7-9 | if (args.Length == 0) |
如果没有参数,提示用户并退出 |
12 | for (int i = 0; i < args.Length; i++) |
遍历参数数组 |
13 | Console.WriteLine(...) |
输出每个参数 |
▶️运行方式及效果
1. 使用命令行运行(假设编译后的程序名为 MyApp.exe
):
MyApp.exe Hello 123 true
2. 输出结果:
你输入的命令行参数为:
参数 0:Hello
参数 1:123
参数 2:true
var关键字
🔍 什么是 var
?
在 C# 中,var
是一个 隐式类型局部变量声明关键字。它告诉编译器根据你赋值的内容自动推断变量的类型。
✅ 注意:
var
并不是“无类型”或“动态类型”,而是静态类型,只是类型由编译器自动推断。
✅代码示例
using System;
class Program
{
static void Main()
{
// 使用 var 声明变量并赋值
var name = "Tom"; // 推断为 string
var age = 25; // 推断为 int
var price = 9.99; // 推断为 double
var isActive = true; // 推断为 bool
var numbers = new int[] { 1, 2, 3 }; // 推断为 int[]
// 输出变量类型和值
Console.WriteLine($"name 类型: {name.GetType()}, 值: {name}");
Console.WriteLine($"age 类型: {age.GetType()}, 值: {age}");
Console.WriteLine($"price 类型: {price.GetType()}, 值: {price}");
Console.WriteLine($"isActive 类型: {isActive.GetType()}, 值: {isActive}");
Console.WriteLine($"numbers 类型: {numbers.GetType()}, 值: {string.Join(",", numbers)}");
}
}
📝 注释说明
行号 | 代码片段 | 注释 |
---|---|---|
8 | var name = "Tom"; |
编译器推断 name 为 string 类型 |
9 | var age = 25; |
推断为 int |
10 | var price = 9.99; |
推断为 double (默认浮点数是 double) |
11 | var isActive = true; |
推断为 bool |
12 | var numbers = new int[] { 1, 2, 3 }; |
推断为 int[] 数组 |
14-18 | Console.WriteLine(...) |
输出变量的类型和值 |
⚠️ 三、使用限制
var
只能用于局部变量(不能用于类字段、属性等)。- 必须在声明时就赋值,否则无法推断类型:
var x; // ❌ 错误:必须有初始化表达式
x = 100; // 依然报错
C# List集合
🧩什么是 List<T>
?
List<T>
是 C# 中一个非常常用的泛型集合类,位于 System.Collections.Generic
命名空间中。它类似于数组,但具有动态扩容能力,可以根据需要自动增加容量。
✅
T
表示集合中元素的类型,如int
、string
、自定义类等。
✅代码示例
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
// 创建一个字符串类型的 List 集合
List<string> names = new List<string>();
// 添加元素
names.Add("张三");
names.Add("李四");
names.Add("王五");
// 插入元素到指定位置
names.Insert(1, "赵六");
// 访问元素(通过索引)
Console.WriteLine("第一个名字是:" + names[0]);
// 遍历集合
Console.WriteLine("所有名字:");
foreach (string name in names)
{
Console.WriteLine(name);
}
// 删除某个元素
names.Remove("李四");
// 检查是否存在某个元素
if (names.Contains("张三"))
{
Console.WriteLine("张三在列表中");
}
// 获取集合长度
Console.WriteLine("当前列表中有 " + names.Count + " 个元素");
}
}
📝 注释说明
行号 | 代码片段 | 注释 |
---|---|---|
5 | using System.Collections.Generic; |
引入泛型集合命名空间 |
8 | List<string> names = new List<string>(); |
创建一个空的字符串列表 |
10-13 | Add() 方法 |
添加元素到列表末尾 |
16 | Insert(1, "赵六") |
在索引 1 处插入元素 |
19 | names[0] |
使用索引访问元素 |
22-26 | foreach 循环 |
遍历列表中的每个元素 |
29 | Remove("李四") |
移除指定元素 |
32 | Contains("张三") |
判断是否包含某个元素 |
36 | Count 属性 |
获取当前集合中元素的数量 |
🧱常用方法和属性一览
方法/属性 | 功能说明 |
---|---|
Add(item) |
添加一个元素到末尾 |
Insert(index, item) |
在指定索引插入元素 |
Remove(item) |
移除第一个匹配的元素 |
RemoveAt(index) |
根据索引移除元素 |
Clear() |
清空整个列表 |
Contains(item) |
判断是否包含某个元素 |
Find(Predicate<T>) |
查找符合条件的第一个元素 |
Sort() |
对列表进行排序 |
Count |
获取当前元素数量 |
C#丢弃
🧩什么是“丢弃”?
你可以把它理解为一个“垃圾桶”,你不关心的数据就扔进去,不会占用内存变量名,也不会引发警告。
✅ 语法:
_
✅ 代码示例
1. 忽略 out
参数
string input = "123";
if (int.TryParse(input, out int number))
{
Console.WriteLine("转换成功:" + number);
}
else
{
Console.WriteLine("转换失败");
}
如果你只关心是否转换成功,而不关心具体值,可以这样写:
if (int.TryParse(input, out _))
{
Console.WriteLine("输入是一个有效的整数!");
}
else
{
Console.WriteLine("输入无效!");
}
🔍 此处我们只关心是否解析成功,所以用
_
丢弃实际解析结果。
2. 忽略元组(tuple)中的某些值
(string name, int age, string city) GetUserInfo()
{
return ("Tom", 25, "Shanghai");
}
var (_, _, city) = GetUserInfo(); // 只取 city,忽略 name 和 age
Console.WriteLine("所在城市:" + city);
3. 在 foreach
或 LINQ 查询中忽略不需要的变量
List<string> names = new List<string> { "Alice", "Bob", "Charlie" };
foreach (var _ in names)
{
Console.WriteLine("这是一个用户");
}
这样可以明确表达:我只关心循环次数,不关心每个元素本身。
4. 使用 _
多次是合法的
var (x, y) = (10, 20);
_ = x;
_ = y;
// 不会报错,因为多个丢弃变量都叫 `_` 是允许的
⚠️ 三、注意事项
内容 | 说明 |
---|---|
_ 不是一个变量 |
不能给它赋值或读取它的值 |
不可变 | 它只是一个占位符,不代表真实数据 |
只能用于 C# 7.0 及以上版本 | .NET Core 2.0 / .NET Framework 4.7+ 支持 |
C#的Range()方法
🧩什么是 Range()
方法?
虽然没有单独的 Range()
方法,但 C# 引入了 范围运算符 ..
和 Range
结构,用于表示一个范围,并结合索引器来提取数据的一部分。
✅ 关键词:
^
表示倒数索引,..
表示范围
✅代码示例
示例代码:
using System;
class Program
{
static void Main()
{
int[] numbers = { 10, 20, 30, 40, 50, 60 };
// 使用 Range 提取索引 [1, 4) 的元素(即索引 1 到 3)
var range1 = numbers[1..4];
Console.WriteLine("range1: " + string.Join(", ", range1)); // 输出:20, 30, 40
// 提取从索引 2 开始到末尾的所有元素
var range2 = numbers[2..];
Console.WriteLine("range2: " + string.Join(", ", range2)); // 输出:30, 40, 50, 60
// 提取从开头到索引 3 的元素(不包含索引 3)
var range3 = numbers[..3];
Console.WriteLine("range3: " + string.Join(", ", range3)); // 输出:10, 20, 30
// 使用 ^ 表示倒数索引:提取倒数第 3 个到倒数第 1 个(即索引 3 到 4)
var range4 = numbers[^3..^1];
Console.WriteLine("range4: " + string.Join(", ", range4)); // 输出:40, 50
}
}
📝 注释说明
行号 | 代码片段 | 注释 |
---|---|---|
7 | int[] numbers = { ... } |
定义一个整型数组 |
10 | numbers[1..4] |
提取从索引 1(含)到 4(不含)之间的元素 |
13 | numbers[2..] |
从索引 2 到末尾 |
16 | numbers[..3] |
从开始到索引 3(不含) |
19 | numbers[^3..^1] |
使用 ^ 表示倒数索引:从倒数第 3 到倒数第 1(不含) |
🔢关键概念:Index
和 Range
C# 8.0 引入两个新结构体:
类型 | 用途 |
---|---|
System.Index |
表示一个位置(支持正向和反向索引) |
System.Range |
表示一个范围 [start..end] |
你可以这样使用它们:
Index start = ^3; // 倒数第三个
Index end = ^1; // 倒数第一个(不包含)
Range range = start..end;
var result = numbers[range];
Console.WriteLine(string.Join(", ", result));
C#常数
🧩什么是 const
常数?
使用 const
关键字声明的变量,在编译时就被确定,并且其值在整个程序中不可更改。
✅ 语法:
public const 数据类型 常量名 = 值;
✅示例代码
using System;
class Program
{
// 定义常数
public const double PI = 3.14159;
public const string APP_NAME = "MyApp";
public const int MaxUsers = 100;
static void Main()
{
Console.WriteLine("圆周率 π = " + PI);
Console.WriteLine("应用程序名称:" + APP_NAME);
Console.WriteLine("最大用户数:" + MaxUsers);
// 尝试修改常数? ❌ 编译错误!
// PI = 3.14; // 报错:无法对静态只读字段赋值
}
}
📝 注释说明
行号 | 代码片段 | 注释 |
---|---|---|
7 | public const double PI = 3.14159; |
定义一个表示圆周率的常量 |
8 | public const string APP_NAME = "MyApp"; |
字符串常量 |
9 | public const int MaxUsers = 100; |
整型常量 |
12-16 | Console.WriteLine(...) |
输出常量值 |
18-19 | PI = 3.14; |
❌ 编译错误:常量不可修改 |
🔤支持的数据类型
C# 中 const
支持以下常用数据类型:
类型 | 示例 |
---|---|
int |
const int Count = 10; |
double / float |
const double Pi = 3.14; |
string |
const string Title = "Hello"; |
bool |
const bool IsReady = true; |
char |
const char Delimiter = ','; |
⚠️ 注意:
const
只能用于 基本数据类型 和某些特殊引用类型(如string
)。
🧱 四、常量与静态只读字段的区别(const
vs static readonly
)
特性 | const |
static readonly |
---|---|---|
编译时常量 | ✅ 是 | ❌ 否 |
运行时初始化 | ❌ 不可 | ✅ 可以 |
修改能力 | ❌ 不可变 | ❌ 初始化后不可变 |
适用类型 | 基本类型、字符串 | 所有类型 |
跨程序集更新影响 | 会保留旧值 | 会同步新值 |
示例对比:
public class Settings
{
public const string NameConst = "Tom"; // 编译时常量
public static readonly string NameReadonly = "Jerry"; // 运行时常量
}
C#字符串格式
🧩C# 中字符串格式化的方法有哪些?
方法 | 特点 |
---|---|
string.Format() |
最基础的格式化方式 |
插值字符串($"..." ) |
C# 6.0 引入,推荐使用 |
Console.WriteLine() 内置支持 |
可直接格式化输出 |
ToString("格式字符串") |
对数字、日期等进行格式化 |
StringBuilder.AppendFormat() |
高效拼接时使用 |
✅1. 使用插值字符串(推荐)
这是目前最常用、也最直观的方式,使用 $""
定义插值字符串。
示例:
string name = "Tom";
int age = 25;
double height = 178.5;
// 使用插值字符串
string info = $"姓名:{name},年龄:{age},身高:{height:F1}cm";
Console.WriteLine(info);
输出:
姓名:Tom,年龄:25,身高:178.5cm
🔍
{height:F1}
表示保留一位小数
✅2. 使用 string.Format()
适合需要延迟格式化或动态构造字符串的场景。
示例:
string message = string.Format("姓名:{0},年龄:{1},身高:{2:F1}cm", name, age, height);
Console.WriteLine(message);
输出同上:
姓名:Tom,年龄:25,身高:178.5cm
✅3. 格式化数字和日期
数字格式化(常见格式说明):
格式 | 示例 | 含义 |
---|---|---|
F2 |
{value:F2} |
保留两位小数 |
N0 |
{value:N0} |
千分位整数 |
P1 |
{value:P1} |
百分比,保留一位小数 |
C |
{value:C} |
货币符号(根据区域自动) |
示例:
double price = 99.5;
double rate = 0.1234;
Console.WriteLine($"价格:{price:C}"); // ¥99.50 或 $99.50
Console.WriteLine($"折扣率:{rate:P1}"); // 12.3%
日期时间格式化:
DateTime now = DateTime.Now;
Console.WriteLine($"完整日期时间:{now:yyyy-MM-dd HH:mm:ss}");
Console.WriteLine($"简写日期:{now:yyyy/MM/dd}");
✅4. 使用 ToString()
格式化
适用于单个变量格式化为字符串:
double value = 1234.5678;
string formattedValue = value.ToString("N2"); // 输出:1,234.57
string percent = (0.876).ToString("P1"); // 输出:87.6%
C#字符串插值
🧩什么是字符串插值?
C# 6.0 引入了 字符串插值(Interpolated Strings),使用 $""
语法,允许你在字符串中直接嵌入变量或表达式,使代码更清晰易读。
✅ 语法:
$"..."
✅ 二、基本使用示例
using System;
class Program
{
static void Main()
{
string name = "Tom";
int age = 25;
double height = 178.5;
// 使用插值字符串输出信息
Console.WriteLine($"姓名:{name},年龄:{age},身高:{height:F1}cm");
}
}
输出:
姓名:Tom,年龄:25,身高:178.5cm
📝逐行注释解释
行号 | 代码片段 | 注释 |
---|---|---|
1 | using System; |
引入基础命名空间 |
3 | class Program |
定义主类 |
5 | static void Main() |
程序入口 |
7-9 | string name = "..."; int age = ...; |
声明变量 |
12 | Console.WriteLine($"...") |
插值字符串,内嵌变量和格式化 |