序列化与反序列化
1. 什么是序列化和反序列化?用途是什么?
// 序列化示例
Person person = new Person { Name = "Alice", Age = 30 };
string json = JsonSerializer.Serialize(person); // 序列化为JSON// 反序列化示例
Person deserialized = JsonSerializer.Deserialize<Person>(json);
- 核心概念:
- 序列化:将对象状态转换为可存储/传输格式(如JSON、XML、二进制)
- 反序列化:将数据流还原为对象
- 关键用途:
- 网络传输(API通信)
- 数据持久化(保存到文件/数据库)
- 进程间通信
- 缓存机制
2. JSON vs XML 序列化的优缺点
特性 | JSON | XML |
---|---|---|
可读性 | 高(轻量级) | 中(标签冗余) |
数据体积 | 小(无标签) | 大(标签占40%+空间) |
解析性能 | 快(比XML快2-10倍) | 慢(DOM解析复杂) |
类型安全 | 弱(无schema) | 强(XSD支持) |
二进制支持 | Base64编码 | 原生支持(CDATA) |
适用场景 | Web API、移动应用 | 企业系统、SOAP服务 |
3. 循环引用处理
// Newtonsoft.Json解决方案
var settings = new JsonSerializerSettings {ReferenceLoopHandling = ReferenceLoopHandling.Ignore
};
string json = JsonConvert.SerializeObject(obj, settings);// System.Text.Json解决方案
options.ReferenceHandler = ReferenceHandler.Preserve;
- 根本原因:对象相互引用形成闭环(如
Parent.Child
和Child.Parent
) - 解决方案:
- 忽略循环引用(
ReferenceLoopHandling.Ignore
) - 使用
[JsonIgnore]
标记属性 - DTO模式(仅序列化必要数据)
- 自定义ID追踪(
PreserveReferencesHandling
)
- 忽略循环引用(
4. 自定义序列化
// 实现ISerializable接口
[Serializable]
public class CustomObject : ISerializable
{public void GetObjectData(SerializationInfo info, StreamingContext context){info.AddValue("customField", _value);}protected CustomObject(SerializationInfo info, StreamingContext context){_value = info.GetString("customField");}
}// JSON自定义转换器
public class DateTimeConverter : JsonConverter<DateTime>
{public override DateTime Read(ref Utf8JsonReader reader, ...) => DateTime.Parse(reader.GetString());public override void Write(Utf8JsonWriter writer, DateTime value, ...)=> writer.WriteStringValue(value.ToString("yyyy-MM-dd"));
}
5. 版本控制策略
- 向前兼容:
- 使用
[OptionalField]
特性 - 反序列化回调(
OnDeserializing
)
- 使用
- 向后兼容:
- 避免删除已序列化字段
- 新字段添加默认值
- 最佳实践:
- 语义版本控制(SemVer)
- 自定义版本字段
- 使用协议缓冲区(protobuf)等版本容忍格式
泛型
1. 泛型核心优势
// 类型安全集合
List<string> strings = new List<string>();
strings.Add("text"); // 编译时类型检查
// strings.Add(42); // 编译错误// 避免装箱
List<int> numbers = new List<int>(); // 无装箱开销
- 三大优势:
- 编译时类型检查
- 消除装箱/拆箱(值类型性能提升20-50%)
- 代码复用(同一算法处理不同类型)
2. 泛型约束类型
public class Processor<T> where T : IComparable, // 接口约束new(), // 构造函数约束struct // 值类型约束
{public void Sort(T[] data) { /*...*/ }
}
约束类型 | 语法 | 应用场景 |
---|---|---|
接口约束 | where T : IDisposable | 确保类型实现特定行为 |
基类约束 | where T : Stream | 限制继承层次 |
构造函数约束 | where T : new() | 创建实例(new T() ) |
值类型/引用类型约束 | where T : struct | 优化值类型操作 |
非托管约束 | where T : unmanaged | 不安全代码/指针操作 |
3. 协变(in)与逆变(out)
// 协变:子类→父类 (out)
IEnumerable<string> strings = new List<string>();
IEnumerable<object> objects = strings; // 安全// 逆变:父类→子类 (in)
Action<object> logObject = obj => Console.Write(obj);
Action<string> logString = logObject; // 安全
logString("text"); // 实际执行logObject
- 核心规则:
- 协变(
out
):返回值类型,支持更具体的子类型 - 逆变(
in
):参数类型,支持更通用的父类型 - 只适用于接口和委托
- 协变(
4. 泛型与反射交互
// 创建泛型类型实例
Type openType = typeof(Dictionary<,>);
Type closedType = openType.MakeGenericType(typeof(int), typeof(string));
object dict = Activator.CreateInstance(closedType);// 调用泛型方法
MethodInfo method = typeof(Serializer).GetMethod("Serialize");
MethodInfo closedMethod = method.MakeGenericMethod(typeof(Person));
closedMethod.Invoke(null, new object[] { person });
5. 泛型接口设计模式
public interface IRepository<TEntity, TKey> where TEntity : IEntity<TKey>
{TEntity GetById(TKey id);void Add(TEntity entity);
}public class UserRepository : IRepository<User, int>
{public User GetById(int id) { /* DB查询 */ }public void Add(User entity) { /* DB插入 */ }
}
集合
1. List vs Array
// Array - 固定大小
int[] array = new int[10];
array[0] = 1; // 赋值
// array[10] = 1; // 运行时越界错误// List<T> - 动态扩展
List<int> list = new List<int>(capacity: 100);
list.Add(1); // 自动扩容
list.RemoveAt(0); // 动态收缩
特性 | Array | List |
---|---|---|
大小 | 固定 | 动态扩展 |
内存分配 | 连续块 | 动态数组+缓冲池 |
插入/删除性能 | O(n) | O(n)(尾部O(1)) |
随机访问 | O(1) | O(1) |
线程安全 | 否 | 否 |
2. Dictionary工作原理
var dict = new Dictionary<string, int>();
dict.Add("key1", 42); // 内部实现伪代码:
// 1. 计算哈希:key.GetHashCode() -> bucketIndex
// 2. 处理冲突:链地址法(链表存储冲突项)
// 3. 存储键值对:entries[bucketIndex] = new Entry(key, value)
- 哈希冲突解决方案:
- 开放寻址法(.NET Core+)
- 链地址法(.NET Framework)
- 时间复杂度:
- 查找/插入/删除:平均O(1),最坏O(n)
- 最佳实践:
- 实现
GetHashCode()
和Equals()
的规范重写 - 设置合理初始容量减少扩容
- 实现
3. 集合接口层次
- 核心接口:
IEnumerable<T>
:基本迭代(GetEnumerator
)ICollection<T>
:添加/删除(Count
,Add
,Remove
)IList<T>
:索引访问(this[index]
,Insert
)ISet<T>
:集合操作(UnionWith
,IntersectWith
)
4. 自定义集合实现
public class ObservableCollection<T> : Collection<T>, INotifyCollectionChanged
{protected override void InsertItem(int index, T item){base.InsertItem(index, item);OnCollectionChanged(NotifyCollectionChangedAction.Add, item, index);}protected virtual void OnCollectionChanged(...) => CollectionChanged?.Invoke(this, args);
}
- 继承
Collection<T>
基类 - 重写关键方法:
InsertItem
,SetItem
,RemoveItem
- 实现通知机制(如
INotifyCollectionChanged
)
5. 线程安全集合
// 并发字典
var concurrentDict = new ConcurrentDictionary<string, int>();
concurrentDict.TryAdd("key", 42);// 生产者-消费者队列
BlockingCollection<int> queue = new BlockingCollection<int>();
// 生产者
queue.Add(1);
// 消费者
int item = queue.Take();
集合类型 | 适用场景 |
---|---|
ConcurrentDictionary | 高频读/写的键值存储 |
ConcurrentQueue | 任务队列(生产者-消费者) |
ConcurrentBag | 无序对象池 |
BlockingCollection | 有界/无界阻塞队列 |
Lambda表达式
1. Lambda vs 匿名方法
// 匿名方法(C# 2.0)
Func<int, int> square = delegate(int x) { return x * x; };// Lambda表达式(C# 3.0+)
Func<int, int> square = x => x * x;
特性 | 匿名方法 | Lambda表达式 |
---|---|---|
语法简洁性 | 冗长 | 简洁 |
参数类型推断 | 需显式声明 | 可推断 |
表达式树支持 | 不支持 | 支持 |
闭包支持 | 支持 | 支持 |
单行返回值 | 需return语句 | 自动返回 |
2. 闭包与变量捕获
int factor = 2;
Func<int, int> multiplier = x => x * factor;
Console.WriteLine(multiplier(5)); // 10// 修改捕获变量
factor = 3;
Console.WriteLine(multiplier(5)); // 15
- 闭包原理:
- 编译器生成隐藏类(closure类)
- 捕获变量转为类的字段
- Lambda成为实例方法
- 陷阱:
- 循环变量捕获问题(所有迭代共享同一变量)
- 解决方案:循环内声明临时变量
3. 表达式树(Expression Trees)
Expression<Func<int, bool>> expr = x => x > 5 && x < 10;// 解析表达式树
BinaryExpression body = (BinaryExpression)expr.Body;
ParameterExpression param = (ParameterExpression)expr.Parameters[0];
- 核心特性:
- 将代码转为可遍历的数据结构
- LINQ to SQL等ORM的核心技术
- 应用场景:
- 动态SQL生成
- 运行时条件组合
- 自定义查询翻译
4. 变量作用域规则
int outer = 10;
Action action = () =>
{int inner = 20;Console.WriteLine(outer + inner); // 访问外部变量
};
// Console.WriteLine(inner); // 错误:inner不可访问
- 作用域层次:
- Lambda参数
- Lambda内部声明
- 外部方法变量
- 类成员变量
- 生命周期:
- 捕获变量的生命周期延至所有委托被GC回收
5. Lambda性能优化
// 避免:高频调用中重复编译表达式树
Expression<Func<Person, bool>> expr = p => p.Age > 18;
Func<Person, bool> compiled = expr.Compile(); // 缓存此委托// 优先使用静态Lambda(C# 9.0+)
Func<int, int> square = static x => x * x; // 不捕获上下文
- 优化策略:
- 缓存编译后的委托
- 避免在高频循环中捕获变量
- 使用静态Lambda减少内存分配
- 避免深层嵌套的Lambda
LINQ
1. 延迟执行 vs 立即执行
// 延迟执行(未真正执行)
IEnumerable<int> query = data.Where(x => x > 5);// 触发执行的操作:
var list = query.ToList(); // 立即执行
int count = query.Count(); // 立即执行
var first = query.First(); // 立即执行// 流式执行(yield return)
foreach (var item in query) // 遍历时逐步执行
{// 每次迭代处理一个元素
}
2. Select vs SelectMany
// Select:一对一转換
var names = persons.Select(p => p.Name);// SelectMany:集合展开
var allPhones = persons.SelectMany(p => p.Phones);// 模拟SQL JOIN
var orders = customers.SelectMany(c => c.Orders,(customer, order) => new { customer.Name, order.Id }
);
3. LINQ性能优化
// 数据库查询优化
var results = context.Users.Where(u => u.Age > 18) // 在数据库执行.AsNoTracking() // 禁用变更追踪.Select(u => new { u.Name }) // 仅选择必要字段.ToList();// 内存集合优化
var local = data.Where(x => x.IsActive).OrderBy(x => x.Name) // 先过滤再排序.Take(20) // 尽早限制数量.ToList();
- 关键优化点:
- 数据库:使用投影减少数据传输
- EF Core:
AsNoTracking()
用于只读查询 - 内存:避免N+1查询模式
- 使用
Any()
替代Count() > 0
4. IQueryable vs IEnumerable
// IEnumerable - 客户端执行
var result = context.Users.AsEnumerable() // 切换为客户端评估.Where(u => ComplexLogic(u)); // 在内存中执行// IQueryable - 数据库执行
var result = context.Users.Where(u => u.Age > 18) // 转换为SQL.ToList(); // 在数据库执行
特性 | IEnumerable | IQueryable |
---|---|---|
执行位置 | 客户端内存 | 数据源(如数据库) |
查询能力 | LINQ to Objects | 支持提供程序翻译(如SQL) |
延迟执行 | 支持 | 支持 |
性能特点 | 适合内存数据集 | 减少数据传输量 |
5. 自定义LINQ提供程序
public class CustomQueryable<T> : IQueryable<T>
{public Type ElementType => typeof(T);public Expression Expression { get; }public IQueryProvider Provider { get; }// 实现GetEnumerator和必要接口
}public class CustomProvider : IQueryProvider
{public IQueryable CreateQuery(Expression expression) { ... }public object Execute(Expression expression){// 解析表达式树var translator = new CustomTranslator();translator.Visit(expression);// 执行自定义查询逻辑return ExecuteDatabaseQuery(translator.GetQuery());}
}
- 实现步骤:
- 实现
IQueryable
和IQueryProvider
- 解析表达式树(
ExpressionVisitor
) - 转换为目标查询语言(如SQL)
- 执行查询并映射结果
- 实现
- 难点:
- 表达式树的完整解析
- 参数化查询处理
- 嵌套查询支持
综合问题深度分析
1. 泛型+集合+LINQ综合应用
public class DataService<T> where T : class
{public List<T> FilterAndSort(List<T> data, Func<T, bool> filter,Func<T, object> sorter){return data.Where(filter).OrderBy(sorter).ToList();}
}// 使用
var userService = new DataService<User>();
var activeUsers = userService.FilterAndSort(users, u => u.IsActive, u => u.LastLoginDate
);
2. 多格式序列化服务
public interface ISerializer<T>
{string Serialize(T obj);T Deserialize(string data);
}public class JsonSerializer<T> : ISerializer<T> { /*...*/ }
public class XmlSerializer<T> : ISerializer<T> { /*...*/ }public class SerializationService<T>
{private readonly ISerializer<T> _serializer;public SerializationService(ISerializer<T> serializer) => _serializer = serializer;public string ToString(T obj) => _serializer.Serialize(obj);public T FromString(string data) => _serializer.Deserialize(data);
}
3. 表达式树工作原理
4. 大型数据集处理策略
// 流式JSON反序列化
await using var stream = File.OpenRead("large.json");
var data = JsonSerializer.DeserializeAsyncEnumerable<Person>(stream);await foreach (var person in data)
{// 单条处理,内存占用恒定
}// 分块处理
const int chunkSize = 1000;
for (int i = 0; i < data.Count; i += chunkSize)
{var chunk = data.Skip(i).Take(chunkSize);ProcessChunk(chunk);
}
5. Lambda实现策略模式
public class PaymentProcessor
{private Func<Order, PaymentResult> _paymentStrategy;public void SetPaymentStrategy(Func<Order, PaymentResult> strategy) => _paymentStrategy = strategy;public PaymentResult ProcessOrder(Order order)=> _paymentStrategy?.Invoke(order) ?? throw new InvalidOperationException();
}// 使用
var processor = new PaymentProcessor();
processor.SetPaymentStrategy(order => order.Total > 1000 ? ProcessCreditCard(order) : ProcessCash(order));