简介:《.NET程序员面试秘笈》是一本专为.NET开发者量身打造的面试指南,内容涵盖.NET框架核心知识、C#高级特性、数据库操作、Web开发、多线程编程及软件工程实践等多个方面。本书结合理论与实战,帮助读者系统复习技术要点,掌握常见面试问题应对策略,提升面试成功率,适用于.NET Framework与.NET Core开发者。
1. .NET框架基础知识
.NET框架是由微软开发的一种软件开发平台,支持多种编程语言,其中C#是最常用的语言之一。它为应用程序开发提供了全面的类库支持和强大的运行时环境。
.NET框架的体系结构与核心组件
.NET框架主要包括公共语言运行时(CLR)、.NET类库、ASP.NET、Windows窗体(WinForms)、Windows Presentation Foundation(WPF)等核心组件。CLR是整个框架的核心,负责代码的执行、内存管理、垃圾回收等任务。
// 示例:一个简单的.NET控制台程序
using System;
namespace HelloDotNet
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello, .NET Framework!");
}
}
}
上述代码展示了在.NET框架下编写的一个最基础的控制台应用程序。 Main 方法是程序的入口点, Console.WriteLine 用于向控制台输出字符串。该程序在CLR环境中运行,体现了.NET框架对托管代码的执行支持。
2. C#面向对象编程(封装、继承、多态)
面向对象编程(Object-Oriented Programming,简称OOP)是C#语言的核心编程范式之一。它通过类和对象来组织代码,使程序结构更清晰、逻辑更严谨、维护更方便。本章将深入探讨C#中的三大核心特性: 封装 、 继承 和 多态 。我们将从基础概念入手,逐步深入到访问控制、构造函数、接口设计、虚方法等进阶内容,并通过代码示例与逻辑分析,帮助读者构建完整的OOP知识体系。
2.1 面向对象编程基础
在C#中,面向对象编程的基础是 类(class)与对象(object) 。类是对象的模板,对象是类的具体实例。理解类与对象的关系,是掌握OOP的第一步。
2.1.1 类与对象的基本定义
在C#中,类使用 class 关键字定义,对象通过 new 运算符创建。以下是一个简单的类定义示例:
public class Person
{
public string Name;
public int Age;
public void SayHello()
{
Console.WriteLine($"Hello, my name is {Name}, I am {Age} years old.");
}
}
逻辑分析:
-
Person是一个类名,表示一个人的基本信息和行为。 -
Name和Age是成员变量(字段),表示对象的属性。 -
SayHello()是成员方法,表示对象的行为。 -
public表示访问权限,允许外部访问。
创建对象并调用方法的示例:
Person p = new Person();
p.Name = "Alice";
p.Age = 30;
p.SayHello(); // 输出:Hello, my name is Alice, I am 30 years old.
参数说明:
-
new Person():创建一个Person类的实例。 -
p.Name = "Alice":设置对象的Name属性。 -
p.SayHello():调用对象的方法。
2.1.2 成员变量与成员方法的访问控制
C#提供了多种访问修饰符来控制类成员的可访问性,包括:
| 修饰符 | 可访问范围 |
|---|---|
public | 所有代码可访问 |
private | 仅类内部可访问 |
protected | 类及其派生类可访问 |
internal | 同一程序集内可访问 |
protected internal | 同一程序集或派生类中可访问 |
示例代码:
public class BankAccount
{
private decimal balance; // 只能在类内部访问
public void Deposit(decimal amount)
{
if (amount > 0)
balance += amount;
}
public decimal GetBalance()
{
return balance;
}
}
逻辑分析:
-
private decimal balance:限制外部直接访问余额字段,避免非法操作。 -
Deposit()方法提供了安全的存入逻辑。 -
GetBalance()方法用于获取余额,遵循封装原则。
2.1.3 构造函数与析构函数的作用
构造函数用于在创建对象时初始化对象的状态,而析构函数用于在对象被销毁时释放资源。
示例代码:
public class Product
{
public string Name { get; set; }
public decimal Price { get; set; }
// 构造函数
public Product(string name, decimal price)
{
Name = name;
Price = price;
}
// 析构函数(不常手动定义)
~Product()
{
Console.WriteLine($"{Name} is being disposed.");
}
}
创建对象并释放资源:
Product p = new Product("Laptop", 999.99m);
Console.WriteLine($"Product: {p.Name}, Price: {p.Price}");
p = null;
GC.Collect(); // 强制垃圾回收,触发析构函数
参数说明:
-
Product(string name, decimal price):构造函数接受参数用于初始化对象。 -
~Product():析构函数在对象被回收前执行,用于资源清理(如文件流、数据库连接等)。 -
GC.Collect():强制执行垃圾回收机制,通常由CLR自动管理。
2.2 封装与继承机制
封装和继承是面向对象编程的两大核心机制。封装用于隐藏实现细节,继承用于实现代码复用。
2.2.1 属性封装与get/set访问器
封装通过属性(property)实现,属性结合 get 和 set 访问器,可以控制字段的读写权限。
示例代码:
public class Student
{
private string name;
public string Name
{
get { return name; }
set
{
if (!string.IsNullOrEmpty(value))
name = value;
else
throw new ArgumentException("Name cannot be empty.");
}
}
}
使用示例:
Student s = new Student();
s.Name = "Bob"; // 调用 set
Console.WriteLine(s.Name); // 调用 get
逻辑分析:
-
private string name:隐藏字段,防止外部直接修改。 -
Name属性提供受控访问,确保数据合法性。
2.2.2 基类与派生类的关系
继承允许一个类从另一个类继承字段和方法。被继承的类称为基类(base class),继承的类称为派生类(derived class)。
示例代码:
// 基类
public class Animal
{
public virtual void MakeSound()
{
Console.WriteLine("Animal sound");
}
}
// 派生类
public class Dog : Animal
{
public override void MakeSound()
{
Console.WriteLine("Woof!");
}
}
使用示例:
Animal a = new Dog();
a.MakeSound(); // 输出:Woof!
逻辑分析:
-
Dog继承自Animal,获得其所有公共成员。 -
override关键字用于重写基类的虚方法,实现多态。
2.2.3 protected和internal访问修饰符的应用
protected 和 internal 用于控制类成员在继承和程序集内的可见性。
示例代码:
public class BaseClass
{
protected int protectedField;
internal int internalField;
}
public class DerivedClass : BaseClass
{
public void AccessFields()
{
protectedField = 10; // 可访问
internalField = 20; // 可访问
}
}
访问权限说明:
-
protectedField:只能在BaseClass及其派生类中访问。 -
internalField:同一程序集内可访问。
2.3 多态与接口设计
多态(Polymorphism)是指同一接口可以有多种实现方式。它通过虚方法、接口和抽象类实现。
2.3.1 虚方法与重写方法的实现
虚方法使用 virtual 定义,派生类通过 override 重写其实现。
示例代码:
public class Shape
{
public virtual void Draw()
{
Console.WriteLine("Drawing a shape.");
}
}
public class Circle : Shape
{
public override void Draw()
{
Console.WriteLine("Drawing a circle.");
}
}
public class Rectangle : Shape
{
public override void Draw()
{
Console.WriteLine("Drawing a rectangle.");
}
}
多态调用:
List<Shape> shapes = new List<Shape>
{
new Circle(),
new Rectangle()
};
foreach (var shape in shapes)
{
shape.Draw();
}
输出结果:
Drawing a circle.
Drawing a rectangle.
逻辑分析:
-
virtual void Draw():定义可被重写的虚方法。 -
override void Draw():派生类提供自己的实现。 - 多态允许统一接口调用不同实现。
2.3.2 接口的定义与实现
接口定义一组方法签名,类或结构体可以实现多个接口,从而支持多重继承。
示例代码:
public interface ILogger
{
void Log(string message);
}
public class ConsoleLogger : ILogger
{
public void Log(string message)
{
Console.WriteLine($"Log: {message}");
}
}
使用示例:
ILogger logger = new ConsoleLogger();
logger.Log("This is a log message.");
逻辑分析:
-
ILogger是一个接口,定义了Log方法。 -
ConsoleLogger实现了该接口,提供具体的日志输出逻辑。
2.3.3 抽象类与接口的比较
抽象类和接口都用于定义行为规范,但它们有本质区别:
| 特性 | 抽象类 | 接口 |
|---|---|---|
| 成员实现 | 可以包含实现 | 只定义签名(C# 8.0后可有默认实现) |
| 构造函数 | 支持 | 不支持 |
| 多重继承 | 不支持(只能继承一个类) | 支持(实现多个接口) |
| 成员访问权限 | 支持 public 、 protected 等 | 默认 public |
| 应用场景 | 共享实现代码 | 定义契约,实现多态 |
示例对比:
// 抽象类
public abstract class Vehicle
{
public abstract void StartEngine();
public void StopEngine()
{
Console.WriteLine("Engine stopped.");
}
}
// 接口
public interface IVehicle
{
void StartEngine();
}
实现类:
public class Car : Vehicle, IVehicle
{
public override void StartEngine()
{
Console.WriteLine("Car engine started.");
}
}
逻辑分析:
-
Vehicle提供部分实现(如StopEngine())。 -
IVehicle纯契约定义,强调行为一致性。 -
Car同时继承抽象类并实现接口,展示多重继承能力。
流程图说明:
下图展示了继承与多态的关系流程:
graph TD
A[Shape] --> B(Circle)
A --> C[Rectangle]
B --> D[Draw()]
C --> E[Draw()]
F[Shape shape = new Circle()] --> G[shape.Draw() => Circle.Draw()]
表格说明:
下表对比了类、接口和抽象类的主要特性:
| 特性 | 类 | 接口 | 抽象类 |
|---|---|---|---|
| 是否可实例化 | ✅ | ❌ | ❌ |
| 是否可有实现 | ✅ | ✅(默认实现) | ✅ |
| 是否可多重继承 | ❌ | ✅ | ❌ |
| 是否可有构造函数 | ✅ | ❌ | ✅ |
| 是否可定义字段 | ✅ | ❌ | ✅ |
| 是否支持访问控制 | ✅ | ✅ | ✅ |
本章通过层层递进的方式,从面向对象的基本概念入手,逐步深入到封装、继承与多态的核心机制,并通过代码示例、表格对比和流程图说明,帮助读者构建完整的OOP知识体系,为后续的高级编程打下坚实基础。
3. C#高级特性(Lambda、LINQ、async/await)
在现代C#开发中,Lambda表达式、LINQ(Language Integrated Query)和 async/await 异步编程模型是提升代码简洁性、可读性和性能的关键特性。它们不仅简化了数据处理与异步操作,还极大地增强了开发者对复杂业务逻辑的掌控能力。本章将从基础语法出发,深入解析这些特性的原理、使用场景及优化策略。
3.1 Lambda表达式与委托
3.1.1 委托的基本语法与使用
委托(Delegate)是C#中实现回调机制的基础,它类似于函数指针,但具备类型安全性。委托可以指向一个或多个方法,并通过委托实例来调用这些方法。
基本语法:
public delegate int Calculate(int a, int b);
使用示例:
public class Program
{
public delegate int Calculate(int a, int b);
public static int Add(int a, int b)
{
return a + b;
}
public static void Main()
{
Calculate calc = Add;
int result = calc(5, 3);
Console.WriteLine(result); // 输出8
}
}
参数说明与逻辑分析:
-
Calculate是一个委托类型,表示一个接受两个整数参数并返回整数的方法。 -
Add方法符合委托签名,可以赋值给Calculate类型的变量。 - 调用
calc(5, 3)实际上是调用了Add方法。
3.1.2 Lambda表达式的写法与应用场景
Lambda表达式是一种简洁的匿名函数写法,广泛用于LINQ查询、事件处理和委托赋值。
基本语法:
(parameters) => expression
使用示例:
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
var evenNumbers = numbers.Where(n => n % 2 == 0);
foreach (var num in evenNumbers)
{
Console.WriteLine(num); // 输出2,4
}
逻辑分析:
-
n => n % 2 == 0是一个Lambda表达式,表示接收一个整数参数n并返回布尔值。 -
Where方法接受一个Func<int, bool>委托,Lambda表达式作为参数传入,筛选出偶数。
3.1.3 Func与Action委托的使用方式
Func<T, TResult> 和 Action<T> 是系统预定义的通用委托类型,分别用于返回值的方法和无返回值的方法。
| 委托类型 | 描述 | 示例 |
|---|---|---|
Func<T1, T2, ..., TResult> | 接收多个参数,返回一个结果 | Func<int, int, int> |
Action<T1, T2, ...> | 接收多个参数,无返回值 | Action<string> |
使用示例:
Func<int, int, int> multiply = (x, y) => x * y;
Console.WriteLine(multiply(4, 5)); // 输出20
Action<string> greet = name => Console.WriteLine($"Hello, {name}");
greet("Alice"); // 输出Hello, Alice
逻辑分析:
-
Func<int, int, int>表示一个接收两个整数并返回整数的函数。 -
Action<string>表示一个接收字符串参数但不返回值的函数。
3.2 LINQ查询语法与执行机制
3.2.1 查询表达式与方法语法的区别
LINQ 提供了两种查询语法:查询表达式(Query Syntax)和方法语法(Method Syntax)。
| 类型 | 特点 | 示例 |
|---|---|---|
| 查询表达式 | 类似SQL语法,适合复杂查询 | from x in list where x > 5 select x |
| 方法语法 | 使用扩展方法链式调用 | list.Where(x => x > 5) |
示例对比:
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
// 查询表达式
var querySyntax = from n in numbers
where n % 2 == 0
select n;
// 方法语法
var methodSyntax = numbers.Where(n => n % 2 == 0);
逻辑分析:
- 查询表达式更易读,适用于复杂查询。
- 方法语法更灵活,适用于链式操作和动态条件。
3.2.2 延迟执行与立即执行的区别
LINQ 查询默认是 延迟执行(Deferred Execution) ,即查询不会立即执行,而是在枚举时才执行。
延迟执行示例:
var query = numbers.Where(n => n > 3);
numbers.Add(6); // 查询尚未执行
foreach (var item in query)
{
Console.WriteLine(item); // 输出4,5,6
}
立即执行示例:
var list = numbers.Where(n => n > 3).ToList();
numbers.Add(7); // 查询已执行
foreach (var item in list)
{
Console.WriteLine(item); // 输出4,5,6
}
流程图说明:
graph TD
A[定义查询] --> B{是否立即执行?}
B -->|是| C[执行并缓存结果]
B -->|否| D[每次枚举时重新执行]
3.2.3 LINQ to Objects与LINQ to SQL的实践
LINQ 可用于多种数据源,其中 LINQ to Objects 针对内存集合, LINQ to SQL 针对数据库查询。
LINQ to Objects 示例:
List<string> names = new List<string> { "Alice", "Bob", "Charlie" };
var filtered = names.Where(n => n.Length > 3).ToList();
LINQ to SQL 示例(EF Core):
using (var context = new AppDbContext())
{
var users = context.Users
.Where(u => u.Age > 18)
.Select(u => u.Name)
.ToList();
}
对比分析表格:
| 特性 | LINQ to Objects | LINQ to SQL |
|---|---|---|
| 数据源 | 内存集合 | 数据库表 |
| 执行方式 | 本地执行 | 转换为SQL远程执行 |
| 性能 | 适用于小数据 | 支持大数据量优化 |
| 延迟执行 | 是 | 是(需支持) |
3.3 异步编程模型(async/await)
3.3.1 async/await关键字的基本用法
async/await 是C#中实现异步编程的核心机制,使异步代码看起来像同步代码,提升可读性和可维护性。
基本结构:
public async Task<int> DownloadDataAsync()
{
using (var client = new HttpClient())
{
string result = await client.GetStringAsync("https://example.com");
return result.Length;
}
}
逻辑分析:
-
async标记方法为异步方法。 -
await等待异步操作完成,释放当前线程资源。 - 返回值为
Task<int>,表示异步操作的结果。
3.3.2 Task与Thread的区别
| 特性 | Thread | Task |
|---|---|---|
| 抽象级别 | 低级线程控制 | 高级任务抽象 |
| 异步支持 | 需手动管理 | 内置支持async/await |
| 返回值 | 无法直接获取 | 支持返回值和异常 |
| 线程池 | 需手动控制 | 自动使用线程池 |
代码对比:
// Thread
Thread thread = new Thread(() => Console.WriteLine("Thread Work"));
thread.Start();
// Task
Task task = Task.Run(() => Console.WriteLine("Task Work"));
await task;
3.3.3 异步操作中的异常处理与ConfigureAwait
异步方法中,异常不会立即抛出,而是通过 Task.Exception 属性捕获。
异常处理示例:
try
{
await DownloadDataAsync();
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
ConfigureAwait说明:
-
ConfigureAwait(false):不恢复原始上下文(如UI线程),适用于库方法。 -
ConfigureAwait(true):默认行为,恢复原始上下文。
string result = await client.GetStringAsync(url).ConfigureAwait(false);
逻辑分析:
- 使用
ConfigureAwait(false)可避免死锁,提高性能,尤其在库代码中推荐使用。 - UI线程或ASP.NET请求上下文需保留上下文时使用
true。
本章通过深入分析Lambda表达式、LINQ查询机制和异步编程模型,帮助开发者掌握现代C#开发的核心技能。下一章将进入.NET类库的实战应用,探讨泛型集合、文件操作与多线程编程等内容。
4. .NET类库使用(System.Collections.Generic、System.IO、System.Threading)
.NET 提供了丰富的类库支持,开发者可以借助这些类库高效地完成数据操作、文件读写、并发处理等任务。本章将重点介绍 System.Collections.Generic 泛型集合类、 System.IO 文件与流操作类,以及 System.Threading 多线程与异步编程类库,帮助开发者深入理解这些常用类库的使用方式与性能优化策略。
4.1 泛型集合与数据结构
在 .NET 开发中,泛型集合是处理数据集合的核心工具。 System.Collections.Generic 命名空间提供了多种高效、类型安全的集合类,如 List<T> 、 Dictionary<TKey, TValue> 、 HashSet<T> 、 SortedDictionary<TKey, TValue> 以及线程安全的 ConcurrentDictionary<TKey, TValue> 等。
4.1.1 List 与 Dictionary 的常用操作
List<T> 是一种动态数组,适合需要频繁添加和访问元素的场景。 Dictionary<TKey, TValue> 是基于哈希表实现的键值对集合,适用于需要通过键快速查找值的场景。
// 示例:List<T> 和 Dictionary<TKey, TValue> 的基本使用
List<string> names = new List<string>();
names.Add("Alice");
names.Add("Bob");
names.Remove("Alice");
Dictionary<int, string> idToName = new Dictionary<int, string>();
idToName.Add(1, "Alice");
idToName[2] = "Bob";
foreach (var name in names)
{
Console.WriteLine(name);
}
foreach (var pair in idToName)
{
Console.WriteLine($"ID: {pair.Key}, Name: {pair.Value}");
}
逐行分析:
-
List<string> names = new List<string>();创建一个字符串类型的列表。 -
names.Add()添加元素。 -
names.Remove()移除指定元素。 -
Dictionary<int, string>创建键为整数、值为字符串的字典。 -
idToName.Add()添加键值对。 -
idToName[2] = "Bob"使用索引器直接赋值。 -
foreach遍历集合,输出结果。
性能对比:
| 集合类型 | 插入效率 | 查找效率 | 删除效率 | 适用场景 |
|---|---|---|---|---|
List<T> | O(1) | O(n) | O(n) | 顺序访问、频繁插入 |
Dictionary<TKey, TValue> | O(1) | O(1) | O(1) | 键值对查找、快速检索 |
4.1.2 HashSet 与 SortedDictionary 的使用场景
HashSet<T> 是一个不包含重复元素的集合,适合用于去重、快速判断是否存在某个元素。 SortedDictionary<TKey, TValue> 是基于红黑树实现的有序字典,适合需要按键排序的场景。
// 示例:HashSet<T> 和 SortedDictionary<TKey, TValue> 的使用
HashSet<string> uniqueNames = new HashSet<string>();
uniqueNames.Add("Alice");
uniqueNames.Add("Bob");
uniqueNames.Add("Alice"); // 不会重复添加
SortedDictionary<int, string> sortedDict = new SortedDictionary<int, string>();
sortedDict.Add(3, "Charlie");
sortedDict.Add(1, "Alice");
sortedDict.Add(2, "Bob");
Console.WriteLine("Unique names:");
foreach (var name in uniqueNames)
{
Console.WriteLine(name);
}
Console.WriteLine("Sorted dictionary:");
foreach (var pair in sortedDict)
{
Console.WriteLine($"ID: {pair.Key}, Name: {pair.Value}");
}
逐行分析:
-
HashSet<string>自动去重,即使添加重复元素也不会报错。 -
SortedDictionary<int, string>会自动按键排序,输出顺序为 1, 2, 3。 - 使用
foreach遍历输出结果。
适用场景对比:
| 集合类型 | 有序性 | 去重性 | 查找效率 | 适用场景 |
|---|---|---|---|---|
HashSet<T> | 否 | 是 | O(1) | 快速去重、集合操作 |
SortedDictionary<TKey, TValue> | 是 | 否 | O(log n) | 按键排序、范围查询 |
4.1.3 线程安全集合类(如 ConcurrentDictionary)
在多线程环境下,普通集合类可能引发线程安全问题。 System.Collections.Concurrent 命名空间提供了线程安全的集合类,如 ConcurrentDictionary<TKey, TValue> ,适用于并发访问的场景。
// 示例:ConcurrentDictionary<TKey, TValue> 的多线程使用
using System.Threading.Tasks;
ConcurrentDictionary<int, string> concurrentDict = new ConcurrentDictionary<int, string>();
Parallel.For(0, 100, i =>
{
concurrentDict.TryAdd(i, $"Value_{i}");
});
foreach (var pair in concurrentDict)
{
Console.WriteLine($"Key: {pair.Key}, Value: {pair.Value}");
}
逐行分析:
- 使用
ConcurrentDictionary替代普通字典,避免线程冲突。 -
TryAdd方法在并发环境中安全添加键值对。 -
Parallel.For模拟多线程并发操作。
线程安全集合类对比:
| 类型 | 是否线程安全 | 是否支持并发读写 | 特点 |
|---|---|---|---|
Dictionary<TKey, TValue> | 否 | 否 | 简单高效,需自行加锁 |
ConcurrentDictionary<TKey, TValue> | 是 | 是 | 支持高并发,适用于多线程环境 |
ConcurrentBag<T> | 是 | 是 | 无序集合,适用于对象缓存等场景 |
4.2 文件与流操作
在 .NET 中, System.IO 命名空间提供了丰富的文件与流操作类,开发者可以使用 FileStream 、 StreamReader 、 StreamWriter 等类进行文件读写操作。此外, Path 和 Directory 类提供了路径与目录的管理功能, BinaryFormatter 、 XmlSerializer 和 JsonSerializer 支持对象的序列化与反序列化。
4.2.1 FileStream 与 StreamReader/StreamWriter 的使用
graph TD
A[打开文件] --> B[创建 FileStream]
B --> C{读写模式}
C -->|读取| D[使用 StreamReader]
C -->|写入| E[使用 StreamWriter]
D --> F[读取内容]
E --> G[写入内容]
F --> H[关闭流]
G --> H
// 示例:FileStream + StreamReader 读取文件
using (FileStream fs = new FileStream("example.txt", FileMode.Open, FileAccess.Read))
using (StreamReader sr = new StreamReader(fs))
{
string content = sr.ReadToEnd();
Console.WriteLine(content);
}
// 示例:StreamWriter 写入文件
using (FileStream fs = new FileStream("output.txt", FileMode.Create, FileAccess.Write))
using (StreamWriter sw = new StreamWriter(fs))
{
sw.WriteLine("Hello, .NET!");
}
逐行分析:
-
FileStream用于打开或创建文件流。 -
StreamReader和StreamWriter分别用于读取和写入文本内容。 -
using语句确保流在使用完毕后自动关闭。
4.2.2 文件路径与目录操作(Path 与 Directory 类)
Path 类提供了路径字符串操作方法,如获取文件名、扩展名、合并路径等。 Directory 类用于操作目录,如创建、删除、遍历目录等。
string path = @"C:\Projects\Example\file.txt";
Console.WriteLine("文件名:" + Path.GetFileName(path)); // 输出 file.txt
Console.WriteLine("扩展名:" + Path.GetExtension(path)); // 输出 .txt
Console.WriteLine("目录名:" + Path.GetDirectoryName(path)); // 输出 C:\Projects\Example
// 创建目录
Directory.CreateDirectory(@"C:\Projects\NewFolder");
// 获取目录下所有文件
string[] files = Directory.GetFiles(@"C:\Projects\Example");
foreach (var file in files)
{
Console.WriteLine(file);
}
常用方法总结:
| 类名 | 方法名 | 用途 |
|---|---|---|
Path | GetFileName | 获取文件名 |
| GetExtension | 获取扩展名 | |
| Combine | 拼接路径 | |
Directory | CreateDirectory | 创建目录 |
| GetFiles | 获取目录下所有文件 |
4.2.3 序列化与反序列化(Binary、XML、JSON)
.NET 支持多种序列化方式,如二进制、XML 和 JSON。以下是一个使用 System.Text.Json 的 JSON 序列化示例:
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
Person person = new Person { Name = "Alice", Age = 30 };
string json = JsonSerializer.Serialize(person);
Console.WriteLine(json); // 输出 {"Name":"Alice","Age":30}
Person deserialized = JsonSerializer.Deserialize<Person>(json);
Console.WriteLine(deserialized.Name); // 输出 Alice
参数说明:
-
JsonSerializer.Serialize():将对象转换为 JSON 字符串。 -
JsonSerializer.Deserialize<T>():将 JSON 字符串反序列化为指定类型的对象。
4.3 多线程与异步操作
在现代应用程序中,多线程和异步编程是提升性能与响应能力的关键手段。 System.Threading 命名空间提供了 Thread 、 ThreadPool 、 Task 等类用于并发编程。
4.3.1 Thread 类与线程生命周期
// 示例:使用 Thread 类创建线程
void PrintNumbers()
{
for (int i = 0; i < 5; i++)
{
Console.WriteLine($"Thread: {i}");
Thread.Sleep(500);
}
}
Thread thread = new Thread(new ThreadStart(PrintNumbers));
thread.Start();
thread.Join(); // 主线程等待子线程完成
Console.WriteLine("Main thread completed.");
逐行分析:
-
ThreadStart是线程执行的方法委托。 -
thread.Start()启动线程。 -
thread.Join()阻塞主线程,直到子线程执行完毕。
4.3.2 ThreadPool 与 Task 的使用对比
ThreadPool 是线程池,用于管理多个线程,适合执行短期任务。 Task 是基于 ThreadPool 的高级抽象,支持异步编程模型。
// 示例:使用 ThreadPool
ThreadPool.QueueUserWorkItem(state =>
{
Console.WriteLine("ThreadPool task executed.");
});
// 示例:使用 Task
Task task = Task.Run(() =>
{
Console.WriteLine("Task executed.");
});
task.Wait(); // 等待任务完成
性能对比:
| 特性 | Thread | ThreadPool | Task |
|---|---|---|---|
| 线程控制 | 强 | 弱 | 中 |
| 异步支持 | 否 | 否 | 是 |
| 异常处理 | 需手动 | 需手动 | 自动 |
| 资源管理 | 手动 | 自动 | 自动 |
4.3.3 异步文件读写与网络请求
异步操作可以避免阻塞主线程,提高应用程序响应速度。以下是一个异步读取文件的示例:
async Task ReadFileAsync(string filePath)
{
using (StreamReader reader = new StreamReader(filePath))
{
string content = await reader.ReadToEndAsync();
Console.WriteLine(content);
}
}
// 调用异步方法
await ReadFileAsync("example.txt");
逻辑分析:
-
async Task表示这是一个异步方法。 -
await reader.ReadToEndAsync()异步读取文件内容。 - 使用
await可以避免阻塞主线程,提升用户体验。
异步网络请求示例:
async Task<string> GetWebContentAsync(string url)
{
using (HttpClient client = new HttpClient())
{
string result = await client.GetStringAsync(url);
return result;
}
}
string html = await GetWebContentAsync("https://example.com");
Console.WriteLine(html.Substring(0, 100)); // 输出前100个字符
总结:
- 使用
async/await可以简化异步代码逻辑。 - 异步操作适用于 I/O 密集型任务,如文件读写、网络请求等。
- 异步代码不会阻塞主线程,有助于提升应用程序的响应能力。
5. .NET Core架构与跨平台开发
.NET Core 是微软推出的跨平台、高性能、模块化的开发框架,其核心设计目标是实现代码的高可移植性、良好的性能表现以及灵活的部署能力。本章将深入剖析 .NET Core 的架构特点,探讨其在跨平台开发中的核心优势,同时围绕项目结构、依赖注入、环境配置、日志管理以及容器化部署等内容,展开系统性的讲解与实践。
跨平台开发的核心优势
为什么选择跨平台开发?
跨平台开发的核心在于“一次编写,到处运行”的理念。.NET Core 的设计初衷就是为了打破 Windows 系统的限制,支持 Linux、macOS 甚至嵌入式设备等多种平台。这种架构优势为开发者带来了以下几个方面的提升:
| 优势维度 | 说明 |
|---|---|
| 灵活性 | 可在多种操作系统上运行,适应不同部署环境 |
| 性能优化 | 更轻量化的运行时,启动速度快,资源占用低 |
| 社区活跃 | 开源项目推动生态快速发展,兼容性强 |
| 云原生支持 | 支持 Docker、Kubernetes 等现代云原生技术 |
| 企业级应用适配 | 可无缝集成微服务、API 网关等架构体系 |
.NET Core 的跨平台实现机制
.NET Core 通过统一的运行时(CoreCLR)和编译器(CoreRT)实现了跨平台的能力。其关键机制如下:
graph TD
A[源代码 C#] --> B[编译为 IL 中间语言]
B --> C[CoreCLR 运行时]
C --> D1[Windows Runtime]
C --> D2[Linux Runtime]
C --> D3[macOS Runtime]
D1 & D2 & D3 --> E[最终平台执行]
如上图所示,C# 源代码被编译为中间语言(IL),然后由 CoreCLR 根据不同平台的运行时进行解释和执行。这种设计保证了程序在不同操作系统上的行为一致性。
实际应用场景举例
以一个跨平台的 Web API 项目为例,开发者可以在 Windows 上使用 Visual Studio 编写代码,然后在 Linux 上部署运行,完全无需修改代码逻辑:
dotnet publish -c Release -r linux-x64
该命令会将项目发布为适用于 Linux 的自包含运行包,便于跨平台部署。
项目结构与.csproj文件的配置
标准项目结构解析
一个典型的 .NET Core 项目结构如下:
MyApp/
├── Program.cs
├── Startup.cs
├── appsettings.json
├── MyApp.csproj
└── Controllers/
└── HomeController.cs
- Program.cs :程序入口,定义
Main方法,创建并运行 Web 主机。 - Startup.cs :配置服务与中间件,定义请求管道。
- appsettings.json :配置文件,存储环境相关参数。
- Controllers/ :存放 MVC 或 Web API 控制器类。
- MyApp.csproj :项目配置文件,定义依赖项、SDK 版本等信息。
.csproj 文件详解
.csproj 文件是 .NET Core 项目的核心配置文件,使用 XML 格式定义项目属性。以下是一个典型的 .csproj 文件内容:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<OutputType>Exe</OutputType>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="5.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.0" />
</ItemGroup>
</Project>
关键配置项说明:
| 配置项 | 说明 |
|---|---|
<TargetFramework> | 指定目标框架版本,如 net5.0、net6.0 等 |
<OutputType> | 定义输出类型, Exe 为可执行程序, Library 为类库 |
<RuntimeIdentifier> | 指定运行时标识符,用于发布自包含应用 |
<PackageReference> | 引用 NuGet 包及其版本 |
多目标框架配置(Multi-targeting)
开发者可以通过修改 .csproj 文件,让项目同时支持多个目标框架,例如:
<TargetFramework>net5.0;netcoreapp3.1</TargetFramework>
这样可以实现一套代码兼容多个 .NET Core 版本,适用于需要长期维护的项目。
依赖注入与中间件的使用
依赖注入(Dependency Injection)机制
.NET Core 内置了轻量级的依赖注入容器,支持构造函数注入、方法注入等多种方式。开发者可以在 Startup.cs 的 ConfigureServices 方法中注册服务:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddSingleton<IMyService, MyService>();
services.AddScoped<IRepository, MyRepository>();
services.AddTransient<IHelper, Helper>();
}
服务生命周期说明:
| 生命周期 | 说明 |
|---|---|
AddSingleton | 整个应用程序生命周期中共享一个实例 |
AddScoped | 每个请求范围内共享一个实例 |
AddTransient | 每次请求都创建一个新实例 |
中间件(Middleware)管道构建
中间件是 ASP.NET Core 中处理请求和响应的核心机制。开发者可以在 Startup.cs 的 Configure 方法中添加中间件:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
每个中间件按照注册顺序依次执行,形成请求处理管道。例如:
graph LR
A[请求] --> B[UseRouting]
B --> C[UseAuthentication]
C --> D[UseAuthorization]
D --> E[UseEndpoints]
E --> F[响应]
ASP.NET Core中的环境配置与日志管理
环境配置(Environment Configuration)
ASP.NET Core 支持通过 appsettings.json 和 appsettings.{Environment}.json 文件进行环境配置。例如:
// appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information"
}
}
}
// appsettings.Development.json
{
"Logging": {
"LogLevel": {
"Default": "Debug"
}
}
}
通过 IConfiguration 接口可以读取配置值:
public class HomeController : ControllerBase
{
private readonly IConfiguration _config;
public HomeController(IConfiguration config)
{
_config = config;
}
[HttpGet]
public string Get()
{
return _config["Logging:LogLevel:Default"];
}
}
日志管理(Logging)
.NET Core 提供了内置的日志接口 ILogger<T> ,支持多种日志提供程序(如 Console、Debug、EventSource 等):
public class MyService
{
private readonly ILogger<MyService> _logger;
public MyService(ILogger<MyService> logger)
{
_logger = logger;
}
public void DoSomething()
{
_logger.LogInformation("执行了 DoSomething 方法");
}
}
日志级别说明:
| 级别 | 说明 |
|---|---|
| Trace | 最详细的日志信息 |
| Debug | 调试信息 |
| Information | 一般信息 |
| Warning | 警告信息 |
| Error | 错误信息 |
| Critical | 严重错误信息 |
跨平台部署与Docker容器化实践
跨平台部署方式
.NET Core 支持以下几种部署方式:
- 依赖框架部署(Framework-dependent) :运行时需安装 .NET Core SDK。
- 自包含部署(Self-contained) :将运行时与应用程序打包在一起,适用于没有 .NET Core 环境的机器。
例如,使用 CLI 发布自包含 Linux 应用:
dotnet publish -c Release -r linux-x64 --self-contained
Docker 容器化部署
Docker 是当前最流行的容器化部署方案。以下是使用 Docker 部署 .NET Core 应用的基本步骤:
1. 编写 Dockerfile
FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build-env
WORKDIR /app
COPY *.csproj ./
RUN dotnet restore
COPY . ./
RUN dotnet publish -c Release -o out
FROM mcr.microsoft.com/dotnet/aspnet:5.0
WORKDIR /app
COPY --from=build-env /app/out .
ENTRYPOINT ["dotnet", "MyApp.dll"]
2. 构建并运行 Docker 镜像
docker build -t myapp .
docker run -d -p 8080:80 myapp
3. 查看容器日志
docker logs <container_id>
容器化部署的优势
| 优势 | 说明 |
|---|---|
| 一致性 | 开发、测试、生产环境一致 |
| 可移植性 | 可部署到任意支持 Docker 的主机 |
| 自动化部署 | 易于与 CI/CD 集成 |
| 资源隔离 | 容器之间互不影响,提高稳定性 |
本章系统地介绍了 .NET Core 的架构特点与跨平台开发实践,涵盖了项目结构、依赖注入、中间件、日志配置以及 Docker 容器化部署等多个核心内容。通过本章的学习,开发者应能够熟练构建、配置并部署跨平台的 .NET Core 应用程序,为后续的微服务架构与云原生开发打下坚实基础。
6. 数据库访问技术与ORM框架
数据库访问是现代应用程序开发中不可或缺的一部分。随着.NET平台的发展,开发者可以使用多种方式来访问和操作数据库,包括传统的ADO.NET和现代的ORM框架如Entity Framework Core(EF Core)。本章将深入讲解这些数据库访问技术的核心机制、使用方法以及性能优化策略,帮助开发者在实际项目中做出合理的技术选型。
6.1 ADO.NET数据库访问
作为.NET平台最早提供的数据库访问技术,ADO.NET提供了直接操作数据库的能力,适用于对性能要求高、需要精细控制SQL执行的场景。
6.1.1 连接字符串与数据库连接管理
连接字符串是建立数据库连接的基础。它通常包含服务器地址、数据库名称、身份验证方式等信息。
string connectionString = "Server=localhost;Database=TestDB;User Id=sa;Password=yourStrong(!)Password;";
参数说明:
-
Server:SQL Server实例的地址; -
Database:要连接的数据库名称; -
User Id和Password:用于认证的用户名和密码; - 对于Windows身份验证,可使用
Integrated Security=true替代用户名和密码。
连接管理技巧:
- 使用
using语句确保连接在使用后自动释放资源; - 合理使用连接池,避免频繁打开/关闭连接。
6.1.2 SqlCommand与SqlDataReader的使用
SqlCommand 类用于执行SQL语句, SqlDataReader 用于高效地读取查询结果。
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
string query = "SELECT Id, Name FROM Users";
using (SqlCommand command = new SqlCommand(query, connection))
{
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
int id = reader.GetInt32(0);
string name = reader.GetString(1);
Console.WriteLine($"ID: {id}, Name: {name}");
}
}
}
}
逻辑分析:
-
SqlConnection建立连接; -
SqlCommand执行查询; -
SqlDataReader遍历查询结果; -
reader.GetInt32(0)表示获取第0列(即Id列)的整数值; - 使用嵌套的
using保证资源释放。
6.1.3 参数化查询与事务处理
参数化查询能有效防止SQL注入,而事务处理则确保多个操作的原子性。
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
SqlTransaction transaction = connection.BeginTransaction();
try
{
string insertQuery = "INSERT INTO Users (Name, Email) VALUES (@Name, @Email)";
using (SqlCommand command = new SqlCommand(insertQuery, connection, transaction))
{
command.Parameters.AddWithValue("@Name", "Alice");
command.Parameters.AddWithValue("@Email", "alice@example.com");
command.ExecuteNonQuery();
}
transaction.Commit();
}
catch (Exception ex)
{
transaction.Rollback();
Console.WriteLine("Transaction rolled back. Error: " + ex.Message);
}
}
逻辑分析:
- 使用
BeginTransaction()开启事务; - 多个SQL命令共享同一事务对象;
- 如果出现异常,调用
Rollback()回滚事务; -
Parameters.AddWithValue添加参数,防止SQL注入。
6.2 Entity Framework Core简介
Entity Framework Core 是微软推出的轻量级ORM框架,支持多种数据库平台,并提供LINQ查询、迁移、上下文管理等高级功能。
6.2.1 EF Core的安装与配置
在项目中使用EF Core,需通过NuGet安装以下包:
-
Microsoft.EntityFrameworkCore.SqlServer -
Microsoft.EntityFrameworkCore.Tools
配置上下文类:
public class AppDbContext : DbContext
{
public DbSet<User> Users { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("Server=localhost;Database=TestDB;User Id=sa;Password=yourStrong(!)Password;");
}
}
逻辑分析:
- 继承
DbContext构建上下文; - 使用
DbSet<T>定义实体集; -
OnConfiguring方法中指定数据库连接。
6.2.2 数据模型的定义与迁移
EF Core支持通过代码生成数据库结构,使用迁移(Migration)机制同步模型变更。
dotnet ef migrations add InitialCreate
dotnet ef database update
数据模型示例:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
流程图展示迁移过程:
graph TD
A[创建实体类] --> B[添加迁移]
B --> C[生成SQL脚本]
C --> D[更新数据库]
6.2.3 查询与更新操作的实现
EF Core支持LINQ查询语法,实现对数据库的灵活操作。
using (var context = new AppDbContext())
{
// 查询
var users = context.Users.Where(u => u.Name.StartsWith("A")).ToList();
// 更新
var user = context.Users.FirstOrDefault(u => u.Id == 1);
if (user != null)
{
user.Email = "newemail@example.com";
context.SaveChanges();
}
}
逻辑分析:
-
Where()生成对应的SQL查询语句; -
ToList()触发查询执行; - 修改实体后调用
SaveChanges()提交更改; - EF Core自动跟踪实体状态并生成UPDATE语句。
6.3 ORM性能优化技巧
尽管ORM简化了数据库操作,但在高并发或大数据量场景下仍需优化以提升性能。
6.3.1 延迟加载与贪婪加载的对比
| 加载方式 | 特点 | 适用场景 |
|---|---|---|
| 延迟加载 | 仅在访问导航属性时加载关联数据,可能导致N+1查询问题 | 关联数据不总是需要时 |
| 贪婪加载 | 使用 Include() 一次性加载主表与关联表数据,减少数据库往返次数 | 需要频繁访问关联数据时 |
示例代码:
// 延迟加载
var user = context.Users.FirstOrDefault(u => u.Id == 1);
var orders = user.Orders; // 触发第二次查询
// 贪婪加载
var userWithOrders = context.Users
.Include(u => u.Orders)
.FirstOrDefault(u => u.Id == 1);
6.3.2 显式加载与查询优化
显式加载适用于按需加载关联数据,避免不必要的查询开销。
var user = context.Users.FirstOrDefault(u => u.Id == 1);
context.Entry(user).Collection(u => u.Orders).Load();
逻辑分析:
- 使用
Entry()获取实体条目; -
Collection().Load()显式加载Orders集合; - 可以结合条件过滤加载特定子集。
6.3.3 EF Core中使用原始SQL的场景
在某些复杂查询或性能瓶颈场景下,可使用原始SQL语句绕过LINQ解析器。
var users = context.Users
.FromSqlRaw("SELECT * FROM Users WHERE Name LIKE 'A%'")
.ToList();
适用场景:
- LINQ不支持的数据库函数或语法;
- 性能敏感的查询;
- 避免EF Core的查询转换开销。
本章系统地介绍了从基础的ADO.NET数据库访问到现代ORM框架EF Core的使用方法,并结合性能优化策略帮助开发者在不同场景下做出合适的技术选择。下一章将深入探讨ASP.NET MVC与Web API的开发实践,为构建Web服务提供理论与实践基础。
7. ASP.NET MVC与Web API开发实践
7.1 MVC模式的核心原理与组件
ASP.NET MVC 是一种基于 Model-View-Controller 模式的 Web 开发框架,它将应用程序逻辑清晰地分离为三个核心组件:
- Model(模型) :处理数据和业务逻辑。通常与数据库交互,例如使用 Entity Framework。
- View(视图) :负责用户界面的呈现,通常使用 Razor 引擎来渲染 HTML。
- Controller(控制器) :接收用户请求,处理输入,并决定将哪个视图返回给用户。
MVC 模式的核心优势在于 职责分离 和 易于测试 ,使得前端与后端开发可以并行进行。
MVC 请求处理流程示意图:
graph TD
A[客户端请求] --> B[路由系统]
B --> C[控制器]
C --> D[模型获取数据]
D --> E[视图渲染]
E --> F[响应返回客户端]
在 ASP.NET MVC 中,控制器继承自 Controller 基类,其方法称为 Action 方法 ,用于处理 HTTP 请求(GET、POST 等)。
示例:一个简单的控制器
public class HomeController : Controller
{
public IActionResult Index()
{
ViewBag.Message = "欢迎使用ASP.NET MVC!";
return View();
}
}
-
IActionResult是一个接口,表示 Action 方法返回的响应结果。 -
ViewBag是一个动态对象,用于传递数据给视图。 -
return View()表示返回视图页面Index.cshtml。
7.2 控制器与视图的交互机制
控制器与视图之间的通信主要通过以下几种方式进行:
- ViewBag / ViewData :用于在控制器和视图之间传递轻量级数据。
- TempData :适用于在多个请求之间临时存储数据(通常用于重定向)。
- 强类型模型传递 :通过将模型类传递给视图,实现类型安全的数据绑定。
示例:通过强类型模型传值
1. 定义模型类
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
2. 控制器中传递模型
public IActionResult ProductDetails()
{
var product = new Product { Id = 1, Name = "笔记本电脑", Price = 8999.99m };
return View(product);
}
3. 视图中接收模型( ProductDetails.cshtml )
@model YourNamespace.Models.Product
<h2>@Model.Name</h2>
<p>价格:@Model.Price</p>
-
@model指令指定视图使用的模型类型。 - 使用
@Model访问模型属性,实现类型安全绑定。
7.3 模型绑定与验证机制
模型绑定(Model Binding)是 ASP.NET MVC 自动将 HTTP 请求数据(如表单、查询字符串、路由参数)映射到控制器方法参数或模型对象的过程。
示例:模型绑定与验证
1. 定义模型并添加验证特性
public class User
{
[Required(ErrorMessage = "姓名不能为空")]
[StringLength(50, MinimumLength = 2)]
public string Name { get; set; }
[EmailAddress(ErrorMessage = "邮箱格式不正确")]
public string Email { get; set; }
}
2. 控制器方法中使用模型绑定与验证
[HttpPost]
public IActionResult Register(User user)
{
if (ModelState.IsValid)
{
// 保存用户逻辑
return RedirectToAction("Success");
}
// 若验证失败,返回原视图并显示错误
return View(user);
}
3. 视图中显示验证错误信息
@model YourNamespace.Models.User
<form asp-action="Register" method="post">
<div>
<label asp-for="Name"></label>
<input asp-for="Name" />
<span asp-validation-for="Name"></span>
</div>
<div>
<label asp-for="Email"></label>
<input asp-for="Email" />
<span asp-validation-for="Email"></span>
</div>
<button type="submit">注册</button>
</form>
-
asp-for是 Tag Helper,用于绑定模型属性。 -
asp-validation-for显示特定字段的验证错误。
7.4 Web API设计原则与RESTful规范
Web API 是构建 HTTP 服务的框架,适用于前后端分离架构。ASP.NET Core Web API 支持构建符合 RESTful 规范的接口。
RESTful API 设计原则包括:
| 原则 | 说明 |
|---|---|
| 使用名词复数 | /api/products 而非 /api/product |
| 使用HTTP方法 | GET(获取)、POST(创建)、PUT(更新)、DELETE(删除) |
| 状态码 | 200(OK)、201(Created)、400(Bad Request)、404(Not Found)、500(Internal Server Error) |
示例:创建一个简单的 Web API 控制器
[ApiController]
[Route("[controller]")]
public class ProductsController : ControllerBase
{
private static List<Product> products = new List<Product>
{
new Product { Id = 1, Name = "手机", Price = 3999.00m },
new Product { Id = 2, Name = "平板", Price = 2999.00m }
};
[HttpGet]
public IActionResult GetAll()
{
return Ok(products);
}
[HttpGet("{id}")]
public IActionResult GetById(int id)
{
var product = products.FirstOrDefault(p => p.Id == id);
if (product == null)
return NotFound();
return Ok(product);
}
[HttpPost]
public IActionResult Create(Product product)
{
products.Add(product);
return CreatedAtAction(nameof(GetById), new { id = product.Id }, product);
}
}
-
[ApiController]标记该控制器用于 Web API。 -
[Route]设置默认路由模板。 -
Ok()、NotFound()、CreatedAtAction()返回标准 HTTP 响应。
7.5 前后端分离架构下的接口设计与测试
在前后端分离架构中,前端通常使用如 Vue.js、React、Angular 等框架,后端提供 Web API 接口。
接口测试工具推荐:
| 工具 | 说明 |
|---|---|
| Postman | 图形化接口测试工具,支持请求构造与响应查看 |
| Swagger (OpenAPI) | 自动生成 API 文档并提供交互式测试界面 |
| curl | 命令行工具,适合自动化测试或调试 |
使用 Swagger 配置 API 文档
1. 安装 NuGet 包
dotnet add package Swashbuckle.AspNetCore
2. 配置 Startup.cs 或 Program.cs
// Program.cs (ASP.NET Core 6+)
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
3. 启动应用后访问:
https://localhost:<port>/swagger
将看到自动生成的 API 接口文档,并可以直接发起测试请求。
简介:《.NET程序员面试秘笈》是一本专为.NET开发者量身打造的面试指南,内容涵盖.NET框架核心知识、C#高级特性、数据库操作、Web开发、多线程编程及软件工程实践等多个方面。本书结合理论与实战,帮助读者系统复习技术要点,掌握常见面试问题应对策略,提升面试成功率,适用于.NET Framework与.NET Core开发者。

624

被折叠的 条评论
为什么被折叠?



