美文网首页ASP.NET
C#8.0之后接口已经不再单纯了

C#8.0之后接口已经不再单纯了

作者: 单程车票_SJ | 来源:发表于2020-10-25 23:27 被阅读0次

1. 讲故事

大家在经过面向对象洗礼的时候,都了解过接口,而且知道它是一种自上而下的设计思路,举个例子,我们电脑上都有 USB 2.0 接口,蓝牙耳机实现了它可以进行充电,移动硬盘实现了它可以在电脑端显示硬盘内容,蓝牙鼠标实现了它可以进行鼠标操控,可以看出USB插口做出来后,谁来实现谁也搞不清楚,实现者能做出什么东西,谁也不知道,这就是接口的魅力,落实在 C# 上就是接口中那一个一个的 stub 方法,留给未来的有缘人去实现,如下代码:

public interface IUsb

    {

        voidExecute();

    }

2. 你可能会有的疑惑

有些朋友可能会说,码农胡言乱语,接口不光可以定义实例方法,还可以定义 属性,索引器,事件 等等。。。如下代码:

public interface IUsb

    {

        event Action<string> action;

        string Name { get; set; }

        string this[string key]

        {

            get; set;

        }

        voidExecute();

    }

哈哈,果然是一个好问题,没错,属性,索引器和事件都可以定义在接口中,但请不要忘了,你列举的这些都是编译器层面的语法糖而已,言外之意就是你看过 编译后的 IL 代码吗?如下图所示:

可以看到,那些所谓的语法糖在IL层面统统是方法,这就很好的解释了为啥接口中只能定义方法的原因。

3. 现在的接口真的变了

然而这种平衡在 C# 8.0 中被打破,现如今的接口除了常规的实例方法,还可以定义任何标记为 static 的字段,属性,方法,构造函数 甚至还可以是 实例方法的默认实现,这就很奇葩了。。。不得不大吼一声,🐂👃, 参考代码如下:

public interface IUsb

    {

        //常量

        public const string constVal = "";

        //静态字段

        public static int age = 20;

        //静态构造函数

        staticIUsb() { }

        //默认方法实现

        voidDisco() { Console.WriteLine("Disco..."); }

        voidExecute();

    }

这下把我搞蒙了,目前除了一些实例字段还不能定义外,其他的都没有问题了,我相信不久的将来 interface 也会把这个遗憾解决掉,/(ㄒoㄒ)/~~ , 这叫我如何向后来的晚辈解释呀~~~  搞的我现在有很多疑惑!

笔者的疑惑

1. 接口的默认方法意义何在?

一个事物的出现,必然有它的应用场景,有些朋友可能会谈到这样的场景,当很多类实现了 IUSB 接口之后,如下代码:

publicinterfaceIUsb

{

voidExecute();

}

publicclassMp4:IUsb

{

publicvoidExecute(){ }

}

publicclassMouse:IUsb

{

publicvoidExecute(){ }

}

由于某些原因我准备在 IUSB 中新增 Disco 方法,这个时候 MP4 和 Mouse 类肯定会报错,大家都知道这是因为没有实现 Disco 的方法,如下图所示:

这个时候该怎么办呢?C# 8.0 的接口默认方法就起到作用了,可以直接在原有接口中定义默认方法,对众多的接口实现者们是无感知的,可以编译成功,如下图所示:

一起都很顺利,接下来我就迫不及待的调用 Disco 方法,代码如下:

我去,从图中看居然说 Mp4 类没有 Disco 方法,这就很莫名其妙了,气人,这叫啥默认方法,为了验证 MP4 类到底有没有 Disco 方法,一个到位的验证方式就是用 windbg 看看 MP4 的方法表。

0:000> !do 0x0000021e63c2ab10

Name:        DataStruct.Mp4

MethodTable: 00007ff7cd972248

EEClass:     00007ff7cd96c5e8

Size:        24(0x18) bytes

File:        E:\net5\ConsoleApp2\ConsoleApp1\bin\Debug\netcoreapp3.1\ConsoleApp1.dll

Fields:

None

0:000> !dumpmt -md 00007ff7cd972248

EEClass:         00007FF7CD96C5E8

Module:          00007FF7CD94F7D0

Name:            DataStruct.Mp4

mdToken:         0000000002000004

File:            E:\net5\ConsoleApp2\ConsoleApp1\bin\Debug\netcoreapp3.1\ConsoleApp1.dll

BaseSize:        0x18

ComponentSize:   0x0

Slots in VTable: 6

Number of IFaces in IFaceMap: 1

--------------------------------------

MethodDesc Table

           Entry       MethodDesc    JIT Name

00007FF7CD8A0090 00007FF7CD870A78   NONE System.Object.Finalize()

00007FF7CD8A0098 00007FF7CD870A88   NONE System.Object.ToString()

00007FF7CD8A00A0 00007FF7CD870A98   NONE System.Object.Equals(System.Object)

00007FF7CD8A00B8 00007FF7CD870AD8   NONE System.Object.GetHashCode()

00007FF7CD8B0670 00007FF7CD972228   NONE DataStruct.Mp4.Execute()

00007FF7CD8B1030 00007FF7CD972238    JIT DataStruct.Mp4..ctor()

从上面最后6行代码可看出,MP4类的方法表中根本就没有 Disco 方法,说明 MP4 的世界里根本就没有这玩意。。。那怎么样才能调用的上呢?你需要将 mp4 转成 IUSB 接口,然后再调用Disco方法就可以了,如下图所示:

可是即使能运行,又有什么用呢?反正子类是感知不到这个接口的默认方法,也颠覆了对接口的认知!我是没有看出有什么好处,水平有限没办法哈。。。

2. 这个场景自有它的解决方案 [扩展方法]

刚才有些朋友提到的场景说后续增加接口方法的时候不影响已实现子类修改代码,其实不需要这个特性 C# 也能实现,毕竟这么庞大的类库代码,肯定会有这样的场景哈,我就拿 List 集合说事,如下代码是 List 的类定义:

publicclassList :IList,ICollection

{

}

publicinterfaceIList :ICollection,IEnumerable,IEnumerable

{

intIndexOf(T item);

voidInsert(intindex, T item);

voidRemoveAt(intindex);

}

publicinterfaceICollection :IEnumerable,IEnumerable

{

voidAdd(T item);

voidClear();

boolContains(T item);

voidCopyTo(T[] array,intarrayIndex);

boolRemove(T item);

}

可以看到 List 实现了 IList 和 ICollection 共 7 个方法,但大家在用 List 编码的时候发现其实远不止这 7 个方法,其他方法的接入(Select,Where)就是通过 C# 特有的 扩展方法 机制实现的,对不对,我觉得扩展方法就可以很好的解决默认接口方法的问题,所以 USB 接口可以用 扩展方法 来实现,如下代码所示:

staticvoidMain(string[] args)

{

varmp4 =newMp4();

mp4.Disco();

Console.ReadLine();

}

publicstaticclassUsbExtension

{

publicstaticvoidDisco(thisIUsb usb)

{

Console.WriteLine("Disco...");

}

}

总的来说,这是一个颠覆我三观的特性,破坏了我对接口的认知,不想再说什么了,大家有什么妙解,欢迎留言~~~

相关文章

  • C#8.0之后接口已经不再单纯了

    1. 讲故事 大家在经过面向对象洗礼的时候,都了解过接口,而且知道它是一种自上而下的设计思路,举个例子,我们电脑上...

  • 我不再单纯了

    我不再单纯了 一切为了结果,目的 小时候 整理一下午的房间 独自欣赏 为纯牛奶走到超市 路上闲逛 帮妈妈跑到很远的...

  • 单纯,不再单纯

    “我爱你” “我喜欢你” 千言万语化作一纸情书, 述说心中满腔爱恋。 校园时代的单纯, 炽热, 只愿你知道, 只为...

  • 不再单纯

    午夜的时分 高高的一座城 远方的人儿苦苦的等 多少回在梦里 向着回家的路一路狂奔 看不见霓虹灯 沿着心的方向将爱点...

  • 不再单纯

    不再向往单纯,而是让心底的单纯唤醒梦魇迷住的躁动与孤寂,于是慢慢的,开始懂得了感恩,懂得了珍惜,懂得了生命中那些真...

  • 因为爱,所以我不得不怕

    从什么时候开始,不再单纯,不再信任,不再勇敢,甚至不敢再轻易的爱。 是受伤了,受罪了,受累了,受苦受难之后吗? 也...

  • 单纯的爱情已经消失了

    自从看了《比悲伤更悲伤的故事》之后,我一直在想,为什么这电影能大火,能引起那么多人的共鸣,能让人潸然泪下?或许就是...

  • 我们不再单纯

    在夏日的清晨,手机在白大褂的口袋里发出一声声响声。那是,特意调的最爱的铃声。可,此时却显得那么突兀.....因为忙...

  • 我已经不再爱你了

    我想,一段感情真正结束的标志,不是其中一个人的离开,而是还在爱的那个人,终于敢承认:走了的那个人,是真的不会再回来...

  • 本季最夯保暖时尚于一身的运动夹克,你都有了吗?

    如今的运动,已经不再是单纯的运动, 工装不再是单纯工装的年代, 单一风格正在面临终结。多元风格的混合,共生,才是发...

网友评论

    本文标题:C#8.0之后接口已经不再单纯了

    本文链接:https://www.haomeiwen.com/subject/wjeomktx.html