介绍

【五分钟的dotnet】是一个利用您的碎片化时间来学习和丰富.net知识的博文系列。它所包含了.net体系中可能会涉及到的方方面面,比如C#的小细节,AspnetCore,微服务中的.net知识等等。
5min+不是超过5分钟的意思,"+"是知识的增加。so,它是让您花费5分钟以下的时间来提升您的知识储备量。

正文

伴随着 .NET Core 3.0 一起发布的 C# 8 ,从发布至今已经过了快大半年了。如果您细心的话,就能发现在C# 8新增的功能中有一条:“默认接口方法” 。半年前当我看到这一新特性的时候,我惊呆了,但是惊讶之余是更多的疑惑。因为对于接口这个东西来说,从C#发布至今的十多年里几乎一直保持它的样子,然而在C# 8之后,它有了巨大的变化。随着而来,也是各种争论的声音。

很早之前我就想写这篇文章了,但是由于各种原因一直拖延到了现在。

先让我们来回顾一下 C# 中原有的接口有什么特点:

  • 接口类似于只有抽象成员的抽象基类。实现接口的任何类或结构都必须实现其所有成员。

  • 接口无法直接进行实例化。其成员由实现接口的任何类或结构来实现。

  • 接口可以包含事件、索引器、方法和属性。

  • 接口不含方法的实现。

  • 一个类或结构可以实现多个接口。一个类可以继承一个基类,还可实现一个或多个接口。

也正是基于这些特点,当我们在接口中为一个方法加上"pulic"等关键字的时候,编译器会提示我们这是一个错误的写法:

复制代码

interface IRepository
{//Compile-time error CS0106 The modifier 'public' is not valid for this item.public void Add();
}

所以更不用谈给方法写一个实现了。这就让它和 C# 中的另外一种事物行成了鲜明的对比,是的,抽象类。不知道大家有没有在各种面试中遇到过这样的提问:“接口能有任何的访问修饰符吗?”,“接口和抽象类的区别是什么?”

曾经您可以和自然的脱口而出答案:“没有修饰符。一个可以有默认方法,一个只能申明方法…………”。但是从现在开始:这些答案是错的了。????

这是微软MSDN中的设计规范截图:

上面的图是我半年前截的图,今天本来想去找对应的链接分享出来,但是发现找不到了。可能…………

新的接口

好了,说了那么多,我们来看看C# 8 为我们改变后的接口是什么样子:

复制代码

enum LogLevel
{Information,Warning,Error
}interface ILogger
{void WriteCore(LogLevel level, string message);void WriteInformation(string message){WriteCore(LogLevel.Information, message);}void WriteWarning(string message){WriteCore(LogLevel.Warning, message);}void WriteError(string message){WriteCore(LogLevel.Error, message);}
}class ConsoleLogger : ILogger
{public void WriteCore(LogLevel level, string message){Console.WriteLine($"{level}: {message}");}
}class TraceLogger : ILogger
{public void WriteCore(LogLevel level, string message){switch (level){case LogLevel.Information:Trace.TraceInformation(message);break;case LogLevel.Warning:Trace.TraceWarning(message);break;case LogLevel.Error:Trace.TraceError(message);break;}}
}ILogger consoleLogger = new ConsoleLogger();
consoleLogger.WriteWarning("Cool no code duplication!");  // Output: Warning: Cool no Code duplication!ILogger traceLogger = new TraceLogger();
consoleLogger.WriteInformation("Cool no code duplication!");  // Cool no Code duplication!

这是我在网上摘取的一部分代码。是的,您没有看错,接口可以实现方法了。并且还可以给它添加上访问修饰符:

复制代码

interface IDemoInterface
{public static int staticIntValue = 123;   //Rightpublic void PulicMethod(){ }  //Right
}

就像您所见的一样,它还可以在内部声明静态的数据。

但是下面的写法依旧会提示错误哦:

复制代码

interface IDemoInterface
{abstract void M1() { } //Error. 因为有abstractabstract private void M2() { } //Errorabstract static void M3() { } //Errorstatic extern void M4() { } //Error.因为有extern
}

争议点

走到这里,也许您会说:“这不挺好的吗?好像对我也没有啥影响。” 确实,假如您不更改接口的签名,无论您是否在接口中增加默认实现还是某些静态数据都不会对已有的应用程序造成任何错误。

但是如果您经常使用抽象类的话,您就会发现,这样的接口是不是和抽象类太像了?甚至有点完全掩盖了抽象类的优势。

当我半年前看到这一新特性时,我就产生了这样的疑惑。这个 “默认方法实现” 的新特性,真的需要吗?如果需要,那我如何选择它和抽象类?

结果我发现,大家都对这一特性产生了困惑:

于时,我抱着怀疑的态度在网上到处搜索答案。最后在C# 官方团队的笔记中我看到了这样一句话:

这句话的意思大致是:我们应该更深入地研究Java在这里所做的事情,Java对接口的实现很好,我们应该…………(有关该说明的github链接可以点击这里)。

我当时心就凉了半截。不过缓了缓,我镇定的思考了一下:好的语言设计被借鉴和参考也是很有必要的。比如现在其它语言都在借鉴C#的await和async。(PS:C#和Typescript怎么越来越像????)。

那么我们真的需要在接口中提供默认实现吗?那什么情况下我需要这样做?毕竟咱们使用了 C# 这么多年,就算接口没有提供默认实现也能设计出很好的系统来。所以为了解决上面的疑问,还是得回到接口和抽象类的本质。

按照咱们以往使用接口和抽象类的情况来看:接口表示的是一种行为,"who can"(比如鸟会飞),而基类表示的是一种类别,"is a"(比如麻雀是鸟)。因此在OOP的世界中,如果咱们细心的来建模的话,我们会把表示行为的共性抽象为一个接口:比如鸟会飞,咱们可以抽象一个IFly的接口。对老版本的 C# 来说,不能提供方法的实现,所以只会有一个Fly() 的方法签名。而现在我们通过新的特性,我们可以给“飞”这个动作提供一个默认的实现,比如 90%的鸟都是“煽动翅膀起飞”,则我们可以将这个大部分 的操作作为默认实现,而对那些10%的 “小众” 进行重写。也正是由于接口更关注的是“行为”,所以接口中不能存在“状态”,因此您会发现虽然可以声明字段了,但是只能声明静态字段。而实例化的状态信息依旧只能通过抽象类来实现。

当然,在现在接口和抽象类建模比较模糊的今天,从技术的实现上来说,其实接口的默认实现并没有带来很多技术编码上的好处。但是如果您坚持好的规范抽象,比如接口开头就是用“I”,将对象的行为进行抽象提升为接口,也许某一刻您会感受到该特性带来的改变。

【5min+】 巨大的争议?C# 8 中的接口相关推荐

  1. C++中的接口(抽象类)

    1.Cpp中的接口(抽象类) 接口描述了类的行为和功能,而不需要完成类的特定实现.接口是使用抽象类来实现的,抽象类与数据抽象互不混淆,数据抽象是一个把实现细节与相关的数据分离开的概念.如果类中至少有一 ...

  2. Java中实现接口与继承的区别

    ** Java中实现接口与继承的区别 ** 首先,先来了解一下什么是接口和继承.接口一般是使用interface来定义的.接口定义同类的定义类似,分为接口的声明和接口体,其中接口体由常量定义和方法定义 ...

  3. Objective-C 入门(七)协议 protocol(JAVA中的接口)

    Objective-C 入门(七)协议 protocol(JAVA中的接口) 接口的作用想必大家都比较了解 OV中的 protocol 相比接口作用相似 语法稍有不同 1.先来看声明一个协议 在创建文 ...

  4. Android中Parcelable接口用法

    --  通过writeToParcel将你的对象映射成Parcel对象,再通过createFromParcel将Parcel对象映射成你的对象.也可以将Parcel看成是一个流,通过writeToPa ...

  5. Java中的接口命名[关闭]

    本文翻译自:Interface naming in Java [closed] Most OO languages prefix their interface names with a capita ...

  6. 初步解读Golang中的接口相关编写方法

    初步解读Golang中的接口相关编写方法 概述如果说goroutine和channel是Go并发的两大基石,那么接口是Go语言编程中数据类型的关键.在Go语言的实际编程中,几乎所有的数据结构都围绕接口 ...

  7. Java6.0中Comparable接口与Comparator接口详解

    Java6.0中Comparable接口与Comparator接口详解 说到现在,读者应该对Comparable接口有了大概的了解,但是为什么又要有一个Comparator接口呢?难道Java的开发者 ...

  8. C# 类中继承接口的属性

    在开发中面对接口编程,有时需要将属性放到接口中,但是在类中又要如何去继承接口的属性呢? 开始的时候,我以为只要继承了接口,就可以将属性拿来用了.代码如下: public interface IA {i ...

  9. java中接口什么时候用_我什么时候应该在java中使用接口?

    在Java中精确使用接口的一个很好的例子将是理想的,适用于任何特定的规则. 看看丹以前所有的问题,他似乎只是逐字逐句地张贴家庭作业/考试问题. 令人惊讶的是,这些不是考试题或其他-今天才找到这个网站, ...

  10. 类与接口(三)java中的接口与嵌套接口

    ###一.接口 ##1. 接口简介 接口: 是java的一种抽象类型,是抽象方法的集合.接口比抽象类更加抽象的抽象类型. 接口语法: [修饰符] [abstract] interface 接口名 [e ...

最新文章

  1. nyoj——297(期望)
  2. html实现点赞评论功能_html的canvas实现画布功能
  3. 支持Dubbo接口文档生成的工具!
  4. 【Android开发】图形图像处理技术-绘制几何图形
  5. su: cannot set user id: Resource temporarily unavailable
  6. 开闭原则coding
  7. 夫妻两一个两年内3张卡9次逾期,一人4次,还能办理房贷吗?
  8. 1007. Maximum Subsequence Sum (25)
  9. 小端字节序和大端字节序
  10. AJAX实现瀑布流布局
  11. Linux学习-账号管理
  12. 终于可以和 QQ 彻底说再见了!
  13. 物联网大数据的爆发只是一个开始
  14. 图解深度学习的笔记:1.MP模型,感知器,BP下的多层感知器
  15. 自调用(自执行)函数的五种写法
  16. android rom签名服务器,【精选】android_ROM分解定制签名教程.pdf
  17. 如何更改win7开机启动画面
  18. wordpress footer.php,wordpress的get_footer( )函数功能详解
  19. 企业微信发送、撤回消息 java代码
  20. 微信运营——利用python自动加微信(通往销冠之路)

热门文章

  1. DEV-aspxgridview中的aspcheckbox
  2. 转:智能卡测试操作系统技术
  3. msdn中C#中常用词汇概念(转帖)
  4. 如何在Firefox 3中重新启用about:config警告消息
  5. shell中字符串操作【转】
  6. 外部中断0(含知识点)
  7. python学习笔记 --- 随机数进阶
  8. setTimeout(function(){}, 0);
  9. LDAP启动cacao提示Invalid file permission
  10. 为EasyUI 的Tab 标签添加右键菜单