显示实现接口的好处c#比java好的地方
所谓Go语言式的接口,就是不用显示声明类型T
实现了接口I
,只要类型T
的公开方法完全满足接口I
的要求,就可以把类型T
的对象用在需要接口I
的地方。这种做法的学名叫做Structural Typing,有人也把它看作是一种静态的Duck Typing。除了Go的接口以外,类似的东西也有比如Scala里的Traits等等。有人觉得这个特性很好,但我个人并不喜欢这种做法,所以在这里谈谈它的缺点。当然这跟动态语言静态语言的讨论类似,不能简单粗暴的下一个“好”或“不好”的结论。
那么就从头谈起:什么是接口。其实通俗的讲,接口就是一个协议,规定了一组成员,例如.NET里的ICollection
接口:
public interface ICollection {
int Count { get; }
object SyncRoot { get; }
bool IsSynchronized { get; }
void CopyTo(Array array, int index);
}
这就是一个协议的全部了吗?事实并非如此,其实接口还规定了每个行为的“特征”。打个比方,这个接口的Count
除了需要返回集合内元素的数目以外,还隐含了它需要在O(1)时间内返回这个要求。这样一个使用了ICollection
接口的方法才能放心地使用Count
属性来获取集合大小,才能在知道这些特征的情况下选用正确的算法来编写程序,而不用担心带来性能问题,这才能实现所谓的“面向接口编程”。当然这种“特征”并不但指“性能”上的,例如Count
还包含了例如“不修改集合内容”这种看似十分自然的隐藏要求,这都是ICollection
协议的一部分。
由此我们还可以解释另外一些问题,例如为什么.NET里的List<T>
不叫做ArrayList<T>
,当然这些都只是我的推测。我的想法是,由于List<T>
与IList<T>
接口是配套出现的,而像IList<T>
的某些方法,例如索引器要求能够快速获取元素,这样使用IList<T>
接口的方法才能放心地使用下标进行访问,而满足这种特征的数据结构就基本与数组难以割舍了,于是名字里的Array就显得有些多余。
假如List<T>
改名为ArrayList<T>
,那么似乎就暗示着IList<T>
可以有其他实现,难道是LinkedList<T>
吗?事实上,LinkedList<T>
根本与IList<T>
没有任何关系,因为它的特征和List<T>
相差太多,它有的尽是些AddFirst
、InsertBefore
方法等等。当然,LinkedList<T>
与List<T>
都是ICollection<T>
,所以我们可以放心地使用其中一小部分成员,它们的行为特征是明确的。
这方面的反面案例之一便是Java了。在Java类库中,ArrayList
和LinkedList
都实现了List
接口,它们都有get
方法,传入一个下标,返回那个位置的元素,但是这两种实现中前者耗时O(1)后者耗时O(N),两者大相近庭。那么好,我现在要实现一个方法,它要求从第一个元素开始,返回每隔P个位置的元素,我们还能面向List
接口编程么?假如我们依赖下标访问,则外部一不小心传入LinkedList
的时候,算法的时间复杂度就从期望的O(N/P)变成了O(N2/P)。假如我们选择遍历整个列表,则即便是ArrayList
我们也只能得到O(N)的效率。话说回来,Java类库的List
接口就是个笑话,连Stack
类都实现了List
,真不知道当年的设计者是怎么想的。
简单地说,假如接口不能保证行为特征,则“面向接口编程”没有意义。
而Go语言式的接口也有类似的问题,因为Structural Typing都只是从表面(成员名,参数数量和类型等等)去理解一个接口,并不关注接口的规则和含义,也没法检查。忘了是Coursera里哪个课程中提到这么一个例子:
interface IPainter {
void Draw();
}
interface ICowBoy {
void Draw();
}
在英语中Draw同时具有“画画”和“拔枪”的含义,因此对于画家(Painter)和牛仔(Cow Boy)都可以有Draw这个行为,但是两者的含义截然不同。假如我们实现了一个“小明”类型,他明明只是一个画家,但是我们却让他去跟其他牛仔决斗,这样就等于让他去送死嘛。另一方面,“小王”也可以既是一个“画家”也是个“牛仔”,他两种Draw都会,在C#里面我们就可以把他实现为:
class XiaoWang : IPainter, ICowBoy {
void IPainter.Draw() {
// 画画
}
void ICowBoy.Draw() {
// 掏枪
}
}
因此我也一直不理解Java的取舍标准。你说这样一门强调面向对象强调接口强调设计的语言,还要求强制异常,怎么就不支持接口的显示实现呢?
这就是我更倾向于Java和C#中显式标注异常的原因。因为程序是人写的,完全不会因为一个类只是因为存在某些成员,就会被当做某些接口去使用,一切都是经过“设计”而不是自然发生的。就好像我们在泰国不会因为一个人看上去是美女就把它当做女人,这年头的化妆和PS技术太可怕了。
我这里再小人之心一把:我估计有人看到这里会说我只是酸葡萄心理,因为C#中没有这特性所以说它不好。还真不是这样,早在当年我还没听说Structural Typing这学名的时候就考虑过这个问题。我写了一个辅助方法,它可以将任意类型转化为某种接口,例如:
XiaoMing xm = new XiaoMing();
ICowBoy cb = StructuralTyping.From(xm).To<ICowBoy>();
于是,我们就很快乐地将只懂画画的小明送去决斗了。其内部实现原理很简单,只是使用Emit在运行时动态生成一个封装类而已。此外,我还在编译后使用Mono.Cecil分析程序集,检查From
与To
的泛型参数是否匹配,这样也等于提供了编译期的静态检查。此外,我还支持了协变逆变,还可以让不需要返回值的接口方法兼容存在返回值的方法,这可比简单通过名称和参数类型判断要强大多了。
有了多种选择,我才放心地说我喜欢哪个。JavaScript中只能用回调编写代码,于是很多人说它是JavaScript的优点,说回调多么多么美妙我会深不以为然——只是没法反抗开始享受罢了嘛……
这篇文章好像吐槽有点多?不过这小文章还挺爽的。
显示实现接口的好处c#比java好的地方的更多相关文章
- Java中的接口(什么是接口,接口的好处,具体的使用)
1.什么是接口? 官方概述: 在java语言中,接口不是类,而是对类的一组需求描述,这些类要遵从接口描述的统一格式进行定义. 这种技术主要用来描述类具有什么功能,而并不给出每个类的具体实现. Bala ...
- 安全接口 interface --显示实现接口
前言:当我们定义接口的成员的时候不需要写访问控制符,因为它是默认public的,也只能是public.当一个类要实现这个接口的时候,自然要公开其成员.一直以来我都这么做. interface Inte ...
- C# 中显示实现接口
接口的实现分为显示实现和隐式实现 用显示实现接口的目的就是为了,当一个类中实现多个具有相同方法的接口时,能够区分开来 在调用的时候,必须用接口调用. class Program { static vo ...
- C#显示声名接口就是为了解决方法重名的问题
class class1 { public static void Main(string[] args) { Person ps = new Person(); ps.KouLan(); IFlya ...
- Atitit.dwr3 不能显示错误具体信息的解决方式,控件显示错误具体信息的解决方式 java .net php
Atitit.dwr3 不能显示错误具体信息的解决方式,控件显示错误具体信息的解决方式 java .net php 1. Keyword/subtitle 1 2. 使用dwr3的异常convert处 ...
- C# 中关于接口实现、显示实现接口以及继承
先列出我写的代码: 接口以及抽象类.实现类 public interface IA { void H(); } public interface IB { void H(); } public abs ...
- 接口和多态都为JAVA技术的核心。
类必须实现接口中的方法,否则其为一抽象类. 实现中接口和类相同. 接口中可不写public,但在子类中实现接口的过程中public不可省. (如果剩去public则在编译的时候提示出错:对象无法从 ...
- .net 反射访问私有变量和私有方法 如何创建C# Closure ? C# 批量生成随机密码,必须包含数字和字母,并用加密算法加密 C#中的foreach和yield 数组为什么可以使用linq查询 C#中的 具名参数 和 可选参数 显示实现接口 异步CTP(Async CTP)为什么那样工作? C#多线程基础,适合新手了解 C#加快Bitmap的访问速度 C#实现对图片文件的压
以下为本次实践代码: using System; using System.Collections.Generic; using System.ComponentModel; using System ...
- C# 显示实现接口
显示实现接口的目的就是为了同名方法. 接口是多实现的,比如说一个方法要实现多个接口,然后这几个接口中有同名方法,这个时候就用到了接口的显示实现. 显示实现接口 成员方法的调用: 接口名.方法名 访问 ...
随机推荐
- 函数式编程语言(Functional Program Language)
(一) 什么是函数编程语言 简单说,"函数式编程"是一种"编程范式"(programming paradigm),也就是如何编写程序的方法论. 是一种编程典范, ...
- php导出excel不知道列数 php26进制函数
function num2Letter($num) { $num = intval($num); if ($num <= 0) return false; $letterArr = array( ...
- apache安装配置
因为个人是在docker上面做实验的,所以可以多少会有些出入. 1.先启动一个docker,配置好基本的工具,网络啊,ssh啊是,tar啊,wget啊,vim等等. 其次去官网获取自己想要的压缩文件的 ...
- hbase 调试各种报错
1.master is initializing 怎么都不知道怎么回事,直接从hbase 2.0 换到了 hbase 2.1 2.java.lang.ClassNotFoundException: o ...
- support:design:26.1.0
https://blog.csdn.net/qzltqdf3179103/article/details/79583491 compileSdkVersion 26buildToolsVersion ...
- 无线LoRa远传智能水表
无线远传智能水表是一款基于瑞萨芯片的水表,该水表具有电子计数.无线远传功能.欠费关阀等功能,无线水表具有的功能如下:无线通信采用SX1278的LoRa进行点对点通信,SX1278模块在通信中启用CAD ...
- IN_ORDER_PLANNING、IN_BOM_CHANGE
一.IN_ORDER_PLANNING 新增一个IN表(IN_ORDER_PLANNING,把ZFP037和ZFP026整合成一张表,标示哪些订单的是真验货/假验货.VIP真验货/假验货订单) ORD ...
- ssh 使用 aws
使用 PuTTY 从 Windows 连接到 Linux 实例 启动您的实例之后,您可以连接到该实例,然后像使用您面前的计算机一样来使用它. 注意 启动实例后,需要几分钟准备好实例,以便您能连接到实例 ...
- openstack(Pike 版)集群部署(二)--- Keystone 部署
一.介绍 参照官网部署:https://docs.openstack.org/keystone/queens/install/ 继续上一博客进行部署:http://www.cnblogs.com/we ...
- HDU4522 湫湫系列故事——过年回家
传送门:点我 中文题面. 思路:拿spfa对卧铺和硬铺分别跑spfa,然后找两个的最短路.体感堆优化的dij也可以,不过spfa跑跑就过去了.有个细节是最后得用long long 存数据,其他的没啥. ...