<p>C# 8.0+ 接口可添加 public 默认方法(如 LogWarning)实现零改动扩展;System.Text.Json 需设 IgnoreUnknownElements=true 防反序列化失败;路径版本控制宜用接口继承+默认方法;弃用字段/方法须用 [Obsolete] 标记并配合可空类型与监控。</p>

用默认接口方法新增功能而不改实现类
旧接口已经上线,客户端大量调用,此时要加一个 LogWarning 方法,又不想让所有 ILogger 实现类都去补实现——直接在接口里加默认方法就行。
关键点在于:C# 8.0+ 允许接口包含具体逻辑,只要不破坏已有契约,实现类就能零改动继续运行。
-
LogWarning必须是public,且带完整实现体(不能是abstract) - 实现类若已定义同名方法,会自动覆盖默认行为;没定义则直接继承
- 不要在默认方法里调用未声明的抽象方法(如
Log是抽象的,就不能在默认LogWarning里直接调它,除非也提供默认实现) - 注意多接口冲突:如果两个接口都定义了
LogWarning默认实现,实现类必须显式重写,并用ILogger1.super.LogWarning()或ILogger2.super.LogWarning()指定来源
字段/参数变更时如何避免 JSON 反序列化失败
后端加了个新字段 Email,但老 Android 客户端发来的 JSON 里没有这个字段,结果反序列化直接抛 JsonException ——这不是代码写错了,是配置没开。
.NET 6+ 的 System.Text.Json 默认严格校验,遇到未知字段就崩。解决办法是告诉序列化器“忽略不认识的字段”。
- 在
Program.cs配置服务时加:options.JsonSerializerOptions.IgnoreUnknownElements = true - 如果用的是
Newtonsoft.Json,对应配置是JsonSerializerSettings.MissingMemberHandling = MissingMemberHandling.Ignore - 新增字段本身要设为可空或带默认值:
public string? Email { get; set; }或public int TimeoutSeconds { get; set; } = 30 - 绝对不要给新字段加
[Required]或非空约束,否则老客户端必 400
路径版本控制 + 接口默认方法组合使用
光靠 URL 版本(如 /api/v1/users、/api/v2/users)能隔离路由,但接口内部逻辑还是可能混在一起。更干净的做法是让 v2 接口继承 v1 接口,并用默认方法补新能力。
比如 IUserApiV1 定义了 GetById(int id),v2 要支持按邮箱查,就定义 IUserApiV2 : IUserApiV1,并在里面加默认方法:
public interface IUserApiV2 : IUserApiV1
{
// 新增能力,老实现类无需修改也能用(只要没重写)
public User? GetByEmail(string email) => throw new NotSupportedException("请在实现类中提供具体逻辑");
}这样既保持 v1 路由只走老逻辑,又让 v2 实现类能选择性覆盖新方法,而旧实现类仍可被 v2 控制器引用(编译通过),只是调用新方法时抛异常——比直接编译报错更可控。
- v2 控制器应注入
IUserApiV2,而非IUserApiV1,否则拿不到新契约 - 若想让旧实现类“假装支持”新方法,就把默认实现写实,比如查数据库时加个
WHERE Email = @email条件 - 别在默认方法里依赖 v2 特有类型(如
Record<T>),否则 .NET Framework 下编译不过
弃用字段和方法的标记方式与实际效果
你准备下线 Status 字段,但不能直接删——得先告诉所有人“别用了”,等三个月后再移除。C# 提供了标准标记机制,但很多人用错。
[Obsolete] 不只是加个警告,它影响编译行为和运行时兼容性:
- 加
[Obsolete("Use NewStatus instead")]:只提示,编译仍过 - 加
[Obsolete("Use NewStatus instead", error: true)]:编译直接报错,适合强制迁移阶段 - 字段上加
[JsonIgnore](Newtonsoft)或[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)](System.Text.Json)可阻止序列化,但反序列化仍会尝试赋值——所以还得配合可空类型 - 别只改文档!客户端 SDK 自动生成工具(如 NSwag)会读取
[Obsolete]注解,自动标灰方法,这才是真实生效点
真正容易被忽略的是:弃用不等于删除。只要字段还在类定义里,JSON 反序列化就会尝试绑定它;只要方法还在接口里,编译器就不会报错——所以弃用期必须双管齐下:标记 + 文档 + 监控调用量,三者缺一不可。


















