未来的.NET之多重继承

通过抽象接口引入有限形式的多重继承,这一.NET新提议颇具争议性。该特性是受Java默认方法(Default Methods)的启发。

默认方法的目的在于允许开发人员修改已发布的抽象接口。修改已发布接口将会产生破坏性的更改,因此在Java和.NET中通常是不允许的。默认方法的提出,为接口编写者提供了一种可重写的实现,缓解了向后兼容问题。

在C#版本的提议中,将包括用于如下部分的语法:

  • 方法体(即“默认”实现);

  • 属性访问器体;

  • 静态方法和属性;

  • 私有方法和属性(默认访问是公开的);

  • 覆写方法和属性。

这个提议并不允许接口具有域,因此形式上是一种有限的多重继承,但避免了一些已在C++中发现的问题(尽管域可以使用ConditionalWeakTable和扩展属性模式模拟)。

用例:IEnumerble.Count

IEnumerable<T>添加Count方法是该特性最广为使用的用例。具体做法并非使用Enumerable.Count这一扩展方法,而是开发人员可以免费获取Count方法,并且如果开发人员能够提供更高效实现的话,能够可选地(optionally)覆写该方法:

interface IEnumerable{int Count(){int count = 0;foreach (var x in this)count++;return count;}
}
interface IList... {    int Count { get; }    override int IEnumerable .Count() => this.Count; } 

正如从上例中可以看到的,实现IList<T>的开发人员无需担心覆写IEnumerable<T>.Count()方法,因为它将自动获得IList<T>.Count

大家所关注的一个问题是该提议会使接口膨胀。既然可以在IEnumerable中添加Count方法,为什么不能添加其他所有的IEnumerable扩展方法?

Eirenarch这样写道:

有人会认真考虑将Count()添加到IEnumerable,对此我有点吃惊。这不是和Reset方法同样的问题吗?并非所有的IEnumerable都可重置,或是可安全地做计数,因为其中的一些接口是一次性的。现在看这个问题,我想不起曾在IEnumerable上使用过Count(),只是在数据库LINQ调用中使用过,因为我不想冒险让Count()消费可枚举类型,或是变得低效。为什么要鼓励更多的Count()

DavidArno补充道:

哈哈,很高兴能看到对这一提议的争论。基础类库(BCL,Base Class Library)团队早就将各种集合类搞得混杂不堪。就这一点而言,我怀疑团队中是否有人真正考虑过Barbara Liskov的建议,她所提出的替换原则如此完全地被打破了。如果将这一提议中的理念赋予团队,这将允许他们造成更大的破坏。想想就十分可怕!

在一次BCL会议上:

“OK,各位,我们想让IEnumerable<T>接口支持cons功能。大家有何建议?”

“这很简单。默认接口方法就能为我们解决这个问题。只需加入(T head, IEnumerable<T> tail) Cons() => throw new NotImplementedException(),这就完事了。IEnumerable的实现者完全可以在闲暇时添加这一实现。”

“非常好,搞定。谢谢大家,本周会议结束。”

注意,LINQ是由另一个独立团队负责的。LINQ的功能并没有计划要切实地迁移到IEnumerable中。

这一更改也会打破当前扩展方法所提供的层次。目前Enumerable.Count方法位于System.Core动态库中,比mscorlib动态库要高两层。可能有人认为将LINQ的部分或完全地加入mscorlib中,会造成该动态库没有必要的膨胀。

另一个批评意见认为这一提议是没有必要的,因为已经存在允许可选地覆写扩展方法的设计模式。

可覆写扩展方法模式

可重新扩展方法依赖于接口检查。理想情况下只需要对一个接口做检查。但是由于一些历史遗留问题,以Enumerable.Count为例,需要检查两个接口。代码如下:

public static int Count(this IEnumerablesource) {    var collectionoft = source as ICollection ;    if (collectionoft != null) return collectionoft.Count;    var collection = source as ICollection;    if (collection != null) return collection.Count;    int count = 0;    using (var e = source.GetEnumerator()) {        while (e.MoveNext()) count++;    }    return count; } 

(为清楚起见,例子中移除了错误处理的代码。)

这个模式的缺点是存在可选接口过于宽泛的问题。例如,如果在类中想要覆写Enumerable.Count方法,那么需要实现整个ICollection<T>接口。对于只读类,则要编写大量的NotSupported异常(重申一下,这里由于历史原因要查看的是ICollection<T>接口,而非更小的IReadOnlyCollection<T>接口)。

默认方法,类的公有API

为了在添加新方法时,为避免向后兼容性问题,不能通过类的公有接口访问默认方法。以IEnumerable.Count为例,看下面的类:

class Countable : IEnumerable{public IEnumeratorGetEnumerator() {…} } 

鉴于并未覆写IEnumerable.Count方法,因此不能这样编写代码:

var x = new Countable();
var y = x.Count();

而是需要做类型转换:

var y = ((IEnumerable)x).Count();

这时为实现在类的公有API上暴露接口方法,需要添加样板代码。这样的做法限制了对类提供默认实现这一技术的有用性。

使用一个默认方法覆写另一个默认方法

一个接口中的默认方法可以覆写另一个接口中的默认方法。这一点可以在IEnumerable.Count用例中看到。

正常情况下,需要对方法显式地指定override关键字,否则新方法在处理上将与其它方法无关。

也可以将一个接口方法标记为“override abstract”。通常没有必要指定abstract关键字,因为所有的抽象接口方法默认就是abstract的。

扩展方法与默认参数的解析顺序

Zippec提出了一个重要问题,即如果新添加的接口方法与用于该接口的扩展方法命名相同时,将会发生什么:

将当前API升级为默认方法时会发生什么?我是否可以认为它们应该比扩展方法在覆写解析上具有更高的优先级?让我们以Count()方法为例。我们可以从IEnumerable上得到该方法吗?如果是这样,它将隐藏使用该特性在C#中重新编译后的LINQ IEnumerable.Count()实现,这是否会更改被调用的代码?我认为对于IQueryable,这是一个问题。

如果该问题存在,为缓解该问题,我们在BCL中以属性方式得到Count方法。由于默认方法会更改任何自定义扩展方法的现有实现,这是否意味着在已经存在的BCL接口上,我们永远无法获得任何默认方法(只能获得属性)?

尽管不常见,一些开发人员的确创建了自己的扩展方法库,镜像了LINQ中的库,但是具有不同的行为。如果扩展方法是作为默认方法移入接口中的,那么就会失去置换扩展库的能力。

用例:INotifyPropertyChanged

下面给出了另一个用例,一般人们在考虑新特性时可能使用:

interface INotifyPropertyChanged
{event PropertyChangedEventHandler PropertyChanged;protected void OnPropertyChanged(PropertyChangedEventArgs args) => PropertyChanged?.Invoke(args);protected void OnPropertyChanged([CallerMemberName] string propertyName) => OnPropertyChanged(new PropertyChangedEventArgs(propertyName));protected void SetProperty(ref T storage, T value, PropertyChangedEventArgs args){if (!EqualityComparer.Default.Equals(storage, value))        {            storage = value;            OnPropertyChanged(args);        }    }    protected void SetProperty (ref T storage, T value, [CallerMemberName] string propertyName) => SetProperty(ref storage, value, new PropertyChangedEventArgs(propertyName)); } 

但是,该用例并不会真正工作,因为接口没有提供生成事件的方法。接口只是定义了事件的Add和Remove方法,没有定义用于存储事件句柄列表的底层代理。

在提议中并未考虑这一问题,因此该问题是可以更改的。通用语言运行平台(CLR)的确为存储事件的“生成Accessor方法”预留了位置,虽然当前仅能使用VB语言。

更多支持的声音

HaloFour写道:

这看上去非常像是一个意识形态上的争论。其中有一些已知的问题自发布.NET 1.0以来,就一直没有被团队很好的解决。长期以来,标准解决方案一直是摆在那里的,但是这些方案常将API弄得完全一团糟,给出了IFoo、IFoo2、IFoo3、IFooEx、IFooSpecial、IFooWithBar这样的内容。扩展方法为解决这些问题做了大量工作,但是局限于那些可以在扩展方法中明确识别和分发的问题。除此之外,扩展方法缺乏特化(Specialization)。

默认实现很好地解决了这些问题。它允许Java团队使用额外的帮助方法(Helper Method)覆写在Java中已长期存在的接口,其中一些的确通过各种实现得以特化,例如Map#computeIfPresent

其它一些批评的声音

HerpDerpImARedditor写道:

噢,该提议会引发那些积习难改的面条式代码(Spaghetti Code)。可能我考虑不周全,敬请谅解,但是这个模式解决了哪些在实现层无法解决的问题?这样的提议看上去抹煞了接口与具体实现之间存在的华丽差异。是否要让IDE完全指定运行时的出处?我不能认为这能与控制反转(IoC)一起工作。

当然,我十分热爱.NET,我历经了从经典的ASP/VB开发背景直至.NET 1。这是第一个我所反对的语言规格添加(虽然当“dynamic”登场亮相时,我在看到它的用例后的确在立场上做了一些让步)。虽然我看见一些人说他们将会忽略存在这一特性,但是我关注的是,可能今后我会在其他人的代码中碰上这样的特性,所以不能无视它。

当然还好,我猜测在任何真正的判决被通过前,我们不会看到这样的特性起作用。

Canthros写道:

这让我很沮丧。

从Github上的讨论看,LINQ的各种扩展方法所实现的一团糟引发了一些不满,尤其是为提供优化实现所必需完成的类型检测(type-sniffing)。虽然这一特性概化到语言特性中可能会大大降低.NET Core实现者的工作,但付出的代价是语言需要承受接口和抽象类之间区分混乱,并且特性中大量吸入了下游存在的问题。

看起来Shapes提议可以大大缓解这个问题,但是现在我无暇切实考虑全部的问题。

原文地址:http://www.infoq.com/cn/news/2017/04/Clr-Default-Methods


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/325900.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

React中的方法调用

onClick{demo}//可以调用函数 onClick{demo()}//返回的是undefide

企业级负载均衡如何实现

转载自 企业级负载均衡如何实现 负载均衡简介 首先&#xff0c;我们来了解一下什么是负载均衡&#xff1a; 在一个大型网站中&#xff0c;在线用户有时可能有几千个甚至上万个之多。如果一个用户的请求需要服务使用0.02秒来处理&#xff0c;那么该服务实例每秒钟将只能处理…

微服务架构师的职责——《微服务设计读书笔记》

如何定义架构师 架构师从英文单词Architect翻译而来&#xff0c;在英文中&#xff0c;Architect原来的意思是“建筑师”。作者吐槽英文中架构师与传统的建筑师单词相同&#xff0c;但实际的工作性质并不相同&#xff0c;以致于在英文的语境中会造成理解上的差异。 传统的建筑师…

Android RaingBar评分条的使用

概述 RatingBar是基于SeekBar和ProgressBar的扩展&#xff0c;用星型来显示等级评定。使用RatingBar的默认大小时&#xff0c;用户可以触摸/拖动或使用键来设置评分&#xff0c;它有两种样式(小风格用ratingBarStyleSmall&#xff0c;大风格用ratingBarStyleIndicator)&#x…

理解C# 4 dynamic(1) - var, object, dynamic的区别以及dynamic的使用

一. 为什么是它们三个? 拿这三者比较的原因是它们在使用的时候非常相似。你可以用它们声明的变量赋任何类型的值。 看看下面的示例: var a 1;object b 1; dynamic c 1; 你还可以使用关键字为它们赋上更加复杂的类型 var a new string[]{"1"};object b new …

获取笔记本的SHA1的值。

开发Android几年来&#xff0c;经常出现这样的情况&#xff0c;每次使用到地图的时候&#xff0c;不知道如何获得笔记本的SHA1值&#xff0c;尤其是在跟换笔记本开发的时候。 因此在CSDN上做一下笔记&#xff01; 进入cmd模式 cd C:\Users\Administrator.android> 输入keyto…

建模:确定服务的边界——《微服务设计》读书笔记

什么样的服务才是好的服务? 高内聚、松耦合的服务才是好的服务。简而言之&#xff0c;就是把相关性强的放在一起&#xff0c;相关性不强的分开&#xff0c;物以类聚&#xff0c;人以群分&#xff0c;服务的划分也是这样。这就需要确定什么要放在一起&#xff0c;什么是要分开的…

谈谈系统稳定性设计

转载自 谈谈系统稳定性设计 一、差旅随想 因为base在分公司&#xff0c;需要经常去总部出差&#xff0c;所以搭乘飞机成了家常便饭&#xff0c;很多时候坐在飞机上会不由的感叹&#xff0c;设计制造这样精密复杂的机器的那帮人真的是了不起&#xff0c;他们是怎样保证这样一台…

Android使用MPAndroidChat

参考文档&#xff1a; https://blog.csdn.net/u013184970/article/details/52095170 https://blog.csdn.net/cen_yuan/article/details/52204281 注意&#xff1a; 在要使用的module的build.gradle添加: dependencies { compile ‘com.github.PhilJay:MPAndroidChart:v3.0.0-be…

微服务集成——《微服务设计》读书笔记

一.理想的集成应该是什么样的&#xff1f; 1.避免破坏性修改 如果在一个微服务的响应中添加一个字段&#xff0c;服务的消费方不应该受到影响。 2.保证API的技术无关性 微服务之间的通信应该是与技术无关的。 3.使服务的消费方易于使用 如果消费方使用该服务比登天还难&…

什么是加密算法

转载自 什么是加密算法 Java的加密知识也是Java常见的领域之一&#xff0c;加密技术的底层确实很复杂&#xff0c;运用了大量的数学知识&#xff0c;要弄明白非常复杂。但是Java语言中运用密码加密工具却是非常简单。我们在Java里面运用这些加密技术&#xff0c;只需要把原理…

Android传感器的使用

Android 中主要的传感器有以下几种 TYPE_ACCELEROMETER 加速度传感器又叫 G-sensor&#xff0c;该数值包含地心引力的影响&#xff0c;单位是 m/s 测量应用于设备 x 、y、z 轴上的加速度。 将手机平放在桌面上&#xff0c;x 轴默认为0&#xff0c;y 轴默认0&#xff0c;z 轴默…

理解C# 4 dynamic(2) – ExpandoObject的使用

ExpandoObject的使用非常简单&#xff0c;很容易入手。上一篇里面已经有详细的介绍了&#xff0c;可以看这里(理解C# 4 dynamic(1) - var, object, dynamic的区别以及dynamic的使用) 下面对ExpandoObject的使用场合和一些认为需要注意的地方&#xff0c;谈一下自己的看法: 一…

谈谈服务限流算法的几种实现

转载自 谈谈服务限流算法的几种实现 保障服务稳定的三大利器&#xff1a;熔断降级、服务限流和故障模拟。今天和大家谈谈限流算法的几种实现方式&#xff0c;本文所说的限流并非是Nginx层面的限流&#xff0c;而是业务代码中的逻辑限流。 为什么需要限流 按照服务的调用方&…

Android如何实现NoActionBar以及Theme.NoTitleBar.Fullscreen效果

效果一 NoActionBar 无论Activity继承自Activity还是AppCompatActivity 只需要在styles中的修改Theme <style name"AppTheme" parent"Theme.AppCompat.Light.NoActionBar"><!-- Customize your theme here. --><item name"colorPrima…

自己动手写一个能操作redis的客户端

转载自 自己动手写一个能操作redis的客户端 引言 redis大家在项目中经常会使用到。官网也提供了多语言的客户端供大家操作redis,如下图所示 但是&#xff0c;大家有思考过&#xff0c;这些语言操作redis背后的原理么&#xff1f;其实&#xff0c;某些大神会说 只要按照redis…

离线安装 VS2017 的正确姿势

国内的网络环境&#xff0c;真的是有很大的不同&#xff0c;有的人装 VS 的时候&#xff0c;号称满速&#xff0c;有的人&#xff08;其实就是我&#xff09;要等它下载很久&#xff0c;还告诉我有个组件没有安装成功。很久很久以前&#xff0c;VS 是提供 ISO 版的离线安装包的…

Android中ImageView的旋转与缩放

说明在代码中已经注释 XML代码 <?xml version"1.0" encoding"utf-8"?> <RelativeLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_parent"android:layout_height"wra…