是否每个.NET中的集合类型都应该实现所有.NET类型接口?

2015 年 1 月 27 日

是否每个.NET 中的集合类型都应该实现所有.NET 类型接口?在1 月14 日进行的.NET 核心API 审查视频中,这一问题在API 相关的重要问题中居首位。这段视频录制了针对.NET 基础类库的十个变更请求的相关讨论。

[视频] GitHub Issue#316:为正则集合(包括 CaptureCollection、GroupCollection 和 MatchCollection)实现 IList、IReadOnlyList和 IList 接口

在.NET 类库中,但凡返回集合类型的属性或方法,多数都会选择使用强类型的集合。这种集合不是诸如 IEnumerable或 IList等类型,而是强类型的 FooCollection。这种方式对于向后兼容来说更为理想,因为可以放心地在类中加入新的方法,而不必依赖于不安全的类型转换。

其实原因还不只是这一条。在.NET 1.0 版本中还没有出现泛型,意味着 IList等接口也不存在,因此只能通过创建自定义的类型来保证类型安全。

当.NET 发布之后,这些强类型中还有很大一部分没有升级为支持泛型集合接口的类型。因此微软所面对的第一个问题就是,是否应该为正则集合实现泛型接口?

第二个问题是,是否应该为只读集合实现 IList等接口?自从.NET 2.0 发布以来,由于只读集合接口的出现,IList应当只用于可变集合类型,这一问题本应迎刃而解。但由于只读集合接口相对较新,有许多 API 依然只支持 IList类型,并仅仅通过文档表示不会对其中的内容进行变更。如果某些方法需要对集合内容进行变更,理论上只需要检查一下 IsReadOnly 属性的值就可以了,但这完全取决于类库开发者和应用程序开发者的自觉性。

再回到原来的问题上,只读的正则集合是否应该支持 IList接口,以便在让不支持 IReadOnlyList接口的遗留 API 也可以调用呢?还是应当选择不支持 IList接口,以减少被某些需要可变的列表的方法所误用呢?

另一个对此问题起到影响的因素是,Windows Forms 中的数据绑定是依赖于 IList 接口的,因此如果要在用户界面中使用这些正则类型,就必须添加该接口。

结论:按照当下的设计指南的做法,为这些类型实现所有接口。从长期的考虑来看,考虑对设计指南做某些调整。

与之相关的另一个问题是,是否应该让这些类型继承于 ReadOnlyCollection类?虽然这种方式能够免去大量的模板代码,但会造成一些向后兼容的问题:

  • 重载的解析会产生变化(这个问题同样会发生在添加新接口的方案中)。
  • 使用反射的结果也会产生变化。

结论:今后,所有新的只读集合都将继承于 ReadOnlyCollection,而现有的类型则保持不变。

序列化是另一个问题。由于序列化类库中的一些历史悠久的 bug 的问题,如果为类型加入对 IList 或其它接口的支持,有可能造成原本正常的序列化失败。

结论:如果造成了序列化的失败,则必须撤消该变更。

[视频] GitHub Issue#110:为XLinq 的doucment 和element 加载加入async 实现

对于这一请求的第一个问题是,应该使用方法重载还是可选参数?对于这个API 来说,问题主要是针对cancellation token 参数而言,但同样的问题也多次出现在其它场合中。

对于可选参数的反对意见在于它们对版本化的支持不理解。使用了可选参数之后,如果为某类型加入了一个接受更多参数的新重载,破坏重载解析的可能性就更大。

结论:可选参数值得一方式,但需要更多的指导与代码分析,以确保它们适当地处理了版本化问题。

第二个问题是,是否要加入一个支持URI 的LoadAsync 方法。整个团队对于该方法的同步版本颇有微词,因为它为XLinq 类库引入了额外的依赖。

结论:如果能够让同步版本的Load(string uri) 方法过期,那么也不需要异步的方法了。否则的话,就需要创建一个对应的异步方法,这样也可以便于将来过渡到async 实现。

第三个问题是,是否应该为Load 方法的所有重载都实现对应的LoadAsync 方法?假设cancellation token 参数依然保留的情况下,这种方法就将导致方法的数量从4 个增加到8 个。而如果cancellation token 变成了可选参数,则LoadAsync 的重载方法的数量将变成16 个,其中的12 个只是跳转到另外4 个主要的重载版本。

结论:首先考虑使用场合最多的方法签名,今后再考虑加入方法重载或可选参数。

[视频] GitHub Issue#400:为 ImmutableArray类型加入 Cast和 CastFrom方法

对于不可变数组来说,它们可能会遇到的转换有三种情景:

  • 静态转换为某个基础类型,这种转换必然会成功。例如将 ImmutableArray转换为 ImmutableArray
  • 动态转换,这种转换有可能会失败。例如将 ImmutableArray转换为 ImmutableArray
  • 有条件地转换为某个继承类型,即大家所熟悉的“as 转换”。

目前只有最后一种转换方式已经得到了完整的支持。通过 ImmutableArray.Create 方法的某个重载可以支持静态转换,但要找到这个重载并不容易,而且这种方法是否会造成额外的内存分配不是一眼就能够看出来的。

对此问题的一个建议是,删除那个不直观的 Create 方法。添加 Cast 和 CastFrom 方法,以实现静态和动态转换操作。

对这个 API 的第一个问题是,是否应该使用 Cast 这个方法名称?主要问题在于,Cast 这个名称与 LINQ 中用于元素延迟转换的方法同名。最理想的方案是将 LINQ 中的方法名称改为 CastElements,但这一点明显是不现实的。

另一个相关的问题是和 As 方法名有关的。通常来说,.NET 会使用 ToXxx 的方法名表示会造成内存分配的转换,而用 AsXxx 的方法名表现无内存分配的转换。而不可变数组中的 As 方法实际上是与 C#中的“as”操作符具有相同的作用,而不是遵循传统意义上的做法。

另一个问题是和 Visual Basic 有关的。与 C#不同,在 VB 中可以通过某个实例变量调用它的静态方法。虽然这种方式让人感觉不爽,但它确实已经成为了 VB 语言中的一部分,因此类似 CastFrom 这样的静态方法就让人搞不清到底是将什么转换为什么。

结论:方法名称依然有改善余地,除此之外,这个提议还是非常有用的。

[视频] GitHub Issue:#394:为ConcurrentDictionary<TKey, TValue> 类型加入GetOrAdd 和AddOrUpdate 方法重载,其中包括一个TArg 参数factoryArgument

接下来登场的是一个pull request 中的内容,即为ConcurrentDictionary 加入新的方法重载。要理解这几个重载的目的,你首先必须理解闭包是如何工作的。当一个闭包产生时,必须对闭包中引用的变量分配内存。如果在一个数量巨大的循环中使用闭包,就会导致大量的内存占用。

与之相反的是,如果某个匿名方法中没有捕捉(capture)任何本地变量,那么指向该方法的委托就能够被编译器进行缓存并重用。这就使得对该方法的调用不会产生内存分配。

这些新的方法重载允许你为GetOrAdd 和GetOrUpdate 方法中所使用的工厂方法加入一个额外的值。一般情况下,这个额外的值足以代替闭包的使用,因此可以减少内存的占用。

这些方法也可以被实现为扩展方法,其性能和普通方法相比基本相同。因此对这个问题的讨论主要在于,该方法带来的性能优势和作为普通方法的便利性,是否能够抵消由此造成的类的体积膨胀的缺点。

一个更广泛的问题是,这种模式是否应该在整个.NET 平台中使用。几乎每个使用了委托的方法,都可以通过这一模式移除对闭包的使用。

结论:如果能够证明新的方法对性能产生了很大的改善,就决定加入这些新方法。

明天我们还将继续对这次API__ 审查会议的分析。

查看英文原文: Should all .NET Collections Implement all .NET Collection Interfaces?

2015 年 1 月 27 日 09:131168
用户头像

发布了 428 篇内容, 共 148.0 次阅读, 收获喜欢 20 次。

关注

评论

发布
暂无评论
发现更多内容

第一周作业

Jam

UML学习笔记

胡家鹏

学习 极客大学架构师训练营

大作业二

Dart Isolate双向通讯

Daniel

架构师训练营第1期 - 第一周课后练习

Anyou Liu

训练营第一周作业

大脸猫

架构师训练营第一期——第一周作业

tao

UML

Architect Training Camp 作业一

陈皓07

第一周学习总结

熊桂平

学习 极客大学架构师训练营

架构师训练营第一期——第一周总结

tao

课程总结

UML练习1

文智

极客大学架构师训练营

第二周作业

Jam

第二周总结

Jam

第一周作业 2——设计文档总结

dll

极客大学架构师训练营

架构师训练营第1期第1周学习总结

好吃不贵

极客大学架构师训练营

架构师一期二班-吴水金-第一课作业

吴水金

架构师技能

第一周作业

大作业2

第一周总结

Jam

训练营第一周总结

大脸猫

架构师训练营Week1作业1

lucian

极客大学架构师训练营

架构师训练营第 1 期第 1 周作业

好吃不贵

极客大学架构师训练营

重新理解“软件工程”

Bruce Talk

软件工程

架构师训练营 01 周 -- 学习总结

骏马

极客大学架构师训练营

作业二-软件架构的简单思考

泡泡

第一周作业1——食堂就餐卡系统设计

dll

极客大学架构师训练营

第一周 架构方法 学习笔记

应鹏

学习 极客大学架构师训练营

Architecture Phase I-Week1 Homework UML Diagram

phylony-lu

极客大学架构师训练营

就餐卡系统UML设计(作业)

胡家鹏

学习

架构师训练营:第一周作业

xs-geek

架构师训练营第 1 期 - 第一周学习总结

Anyou Liu

是否每个.NET中的集合类型都应该实现所有.NET类型接口?-InfoQ