关于c#:小失误大问题-为已发布的接口更名

59次阅读

共计 2919 个字符,预计需要花费 8 分钟才能阅读完成。

写代码不免呈现失误。在对某些曾经公布的库进行降级或者审查的时候,就有可能会发现一些接口名称须要变更。比方,晚期命名不合乎特定标准,或者呈现了难以发现的拼写错误等。有错当然是要改的,然而间接更名会影响到已公布的接口。粗犷的名称变更实质上是删除了旧接口,创立了新接口,对 API 用户来说极具破坏性 —— 用户会发现所有用到这些接口的中央都编译不过,或者不能运行了,这几乎就是一场劫难。

本文次要以 C# 为例介绍对库接口更名的解决 —— 在 Assembly 外部,间接应用“重命名”重构办法,借助 IDE/Editor 的能力就能够实现变更;然而对于凋谢进来的接口,就必须放弃接口兼容性,并申明过期工夫。

本文以 C# 为例,然而解决形式和重构思维是语言无关的!

解决不符合规范的类属性命名

这一节次要是解决属性更名的问题。办法更名和属性更名是同样的情理,前面不再赘述。

假如有如下定义:

public class ActionResult : IActionResult {public int code { get; set;}
    public string message {get; set;}
}

这个定义命名不合乎“公开属性应用 Pascal Case 命名规定”的标准。但作为一个曾经被宽泛应用的库,间接重命名将产生微小的毁坏,所以这里应该按正确的名称增加属性,并将旧的属性申明为过期。

public class ActionResult : IActionResult {public int Code { get; set;}
    public string Message {get; set;}
    
    [Obsolete("因标准命名,将从 v3.4 版本中删除,请应用 Code 代替")]
    public int code {
        get => Code;
        set => Code = value;
    }
    
    [Obsolete("因标准命名,将从 v3.4 版本中删除,请应用 Message 代替")]
    public string message {
        get => Message;
        set => Message = value;
    }
}

不过批改后的 ActionResult 会在序列化的时候呈现问题。库中应用了 Newtonsoft JSON 来失去 JSON,按我的项目中的 JSON 标准,键名会应用 Camel Case 命名规定,所以在配置中增加了 CamelCasePropertyNamesContractResolver 实例。那么 ActionResultCodecode 属性都会被解决为 "code" 这个键名,于是产生了重名的抵触。

为了解决这个问题,能够为重名属性中的一个增加 Newtonsoft JSON 的 [JsonIgnore] 个性申明。为了放弃兼容性,应该在新加的 CodeMessage 属性上增加这一申明,并在 Obsolete 形容和相干文档、发行阐明中充分说明这一状况,通知用户这一变更可能产生序列化问题。

兴许你曾经发现了,在这个问题上,咱们作为库的发行方始终不能放弃齐全的兼容,毕竟用户应用什么样的序列化形式不是咱们能决定的。兴许用户会序列化成 XML,兴许用户不应用 Newtonsoft JSON 而是应用别的 JSON 序列化工具,兴许用户是须要遍历所有属性来实现某种逻辑 —— 作为库的发行方,咱们除了告诉和正告,无能为力 —— 这也是很多出名大库会把一些奇怪的接口保留很久的起因之一。

解决不符合规范的接口属性

留神到,下面示例的类实现了 IActionResult 接口。codemessage 的命名标准问题其实是从接口引入的,所以咱们还须要解决接口属性的命名。解决接口属性会更为繁琐,但好在 C# 8.0 引入了接口默认实现的个性。假如之前 IActionResult 接口定义如下

public interface IActionResult {int code { get; set;}
    string message {get; set;}
}

应用默认实现增加 CodeMessage 并申明原来的属性行将生效

public interface IActionResult {int Code { get => code; set => code == value;}
    string Message {get => message; set => message = value;}

    [Obsolete("因标准命名,将从 v4.0 版本中删除,请改为实现 Code 属性")]
    int code {get; set;}

    [Obsolete("因标准命名,将从 v4.0 版本中删除,请改为实现 Message 属性")]
    string message {get; set;}
}

变更之后,前述 ActionResult 规范化前后的代码都能够通过编译。

这里特地须要留神的是,接口变更影响会比实现(类)的影响更大,应该给予用户足够的批改工夫 来解决,通常会在下个大版本或者越过几个大版本之后才理论删除申明为 Obsolete 的接口,进行破坏性的降级。

解决类名中的拼写错误

在代码审查的过程中,发现类 ApiErrorActioin 的名字中呈现了拼写错误(查究为什么会让这样的谬误呈现在已公布的库中是有必要的,因为这通常是因为过程治理中存在破绽,但不是本文的钻研内容)。

间接更改类名同样是极具破坏性的,咱们能够应用“重命名”重构办法更正名称,再增加一个与之存在继承关系,但放弃原来谬误名称的类来解决。

先应用重命名重构办法将 ApiErrorActioin 更正名称为 ApiErrorAction。当初所有 Assembly 内的谬误名称都更正了。而后再定义一个继承自 ApiErrorActionApiErrorActioin(留神要实现与原来雷同签名的所有构造函数重载),并增加废除申明。

[Obsolete("因为拼写错误将从 v3.5 版本中删除,请应用正确命名的 ApiErrorAction")]
class ApiErrorActioin : ApiErrorAction {
    // 构造函数示例
    public ApiErrorActioin(string name): base(name) {}}

这里须要特地留神的是,为放弃兼容性而存在的 ApiErrorActioin 除了放弃原来的非公有构造函数接口之外,其余接口都能够间接从修改后的 ApiErrorAction 继承而来。而且,这个要废除的 ApiErrorActioin 不应该蕴含任何逻辑代码

对接口更名也能够采纳相似的办法。但要留神的是,这种解决形式增加了继承档次。尽管个别状况下不会造成用户的困扰,然而如果用户的“反射”代码有波及到继承档次的逻辑,就有可能呈现问题。因而这样的变更同样须要在文档中注明并收回正告。

小谬误,大问题

对于人类来说,大脑的主动修改能力十分强(真正的人工智能!)所以一个小小的拼写错误或者大小字谬误并不是什么大问题。但对计算机来说,只差一个字符,那就是齐全不同的两个标识。更正拼写自身是个小事,但对于公共库接口的更名,可能会对用户产生微小的影响,须要十分审慎地解决。

然而,即便咱们十分审慎的解决了能想到的各种兼容性问题,差别依然是不可避免的。你永远不晓得用户会怎么应用这个库,所以也不晓得用户会因为变更遇到什么奇怪的问题 —— 所以请器重文档和发行阐明中的详细描述和正告。问题是藏不住的,颁布它才是正确的抉择。

正文完
 0