• 论C#的多继承


    意外中看到这篇博客,很有意思!

    本文转自:http://www.cnblogs.com/leotsai/p/csharp-multi-inheritance.html

    C#多继承的讨论似乎是个古老的问题了,但今天本文要向大家展示的C#多继承可能是大家闻所未闻见所未见的,甚至是发明C#语言的人也不曾想到我会这样去写代码,并且自得其乐。

    说起多继承,首先大家可以想想这个问题:你知道在C#中怎么实现多继承吗?

    主流的答案无非2种。

    答案一:用接口啊,一个类可以继承自多个接口的。

    答案二:C#不支持多继承,C++才支持多继承,多继承会让代码变得很乱,因此微软在设计C#的时候放弃了多继承。

    能够知道答案二的人显然懂的更多,我也在很长一段时间内相信C#不支持多继承,直到2013年5月的一个项目中,我偶然的发现自己的代码就完全实现了真正意义的多继承。

    先说说什么是真正意义的多继承。真正的多继承应该是像C++那样的,而不是说像在C#里面一个类继承了多个接口就叫多继承。在C#中,如果一个类实现了多个接口,那么要为每个接口写实现,如果接口被多个类继承,那么就会有重复的代码,这显然是无法接受的。

    然而C++那样的多继承也确确实实给编码带来了很大的麻烦,我也相信微软真的是因为意识到了多继承的不合理之处才在C#中摈弃了这个特性。而我在C#中实现的多继承,第一是真正的多继承,第二代码写的很合理。

    请看案例

    假如你有一个类叫老虎,还有一个类叫苍蝇。现在你想新创一个超级老虎类,一种可以飞的老虎。在C++中,你可以定义一种超级老虎类,让其继承自老虎和苍蝇,这样这种老虎就可以飞了。然而,问题出现了,这种超级老虎由于同时也继承自苍蝇,而苍蝇下面有个方法叫吃,参数类型是屎。吃屎的这个方法显然跟我们的超级老虎太不搭了。

    虽然这个例子有些夸张,但是很多C++程序员真的就是这样在设计代码。由于子类继承了多个父类,而多个父类肯定有些成员跟这个子类不搭调,于是子类的调用者就很难受了。比如上面这个例子,当调用者拿到超级老虎的一个实例时,发现超级老虎下面怎么会有个吃屎的方法呢!!!真的是要笑死人了。

    C++要这样允许多继承就必然会造成这个问题。C#程序员就绝对不会写出这样滑稽的代码。对于C#程序员,肯定是要把这个飞的方法提成接口的,然后让苍蝇类和超级老虎类都继承自这个接口。这样,苍蝇会飞,超级老虎也会飞。是不是完美解决这个问题?

    问题看上去解决了,但是,假如我跟你说苍蝇飞的方法跟超级老虎飞的方法需要一模一样:首先张开双翅,身体前倾,拍打双翅,起飞,继续拍打。我们肯定不能把同一份代码copy一份吧,那是属于入门级程序员干的事,我们现在已经没资格干那事了。那怎么办呢?简单快速的做法是使用静态方法,比如FlyHelper.Fly(...)。

    静态方法解决了代码重用的问题,但写起来始终觉得哪里不对劲。我的超级老虎类和苍蝇都明明继承了飞了啊, 为什么还要这样调用一句静态方法。如果以后哪天我想让我的猪也能飞起来,那岂不是还要来调用这个静态方法。

    到底怎样才能在C#中实现像C++那样优雅的继承呢? 

    答案揭晓

    答案其实很简单,那就是给IFly接口写扩展方法。

    首先请看这个空接口的定义,及其扩展方法(注意泛型限制):

    复制代码
     1 public interface I飞
     2 {
     3         
     4 }
     5 
     6 public static class 飞接口的扩展
     7 {
     8     public static void 飞<T>(this T 飞实例) where T : I飞
     9     {
    10         Console.WriteLine("准备");
    11         Console.WriteLine("张开双翅");
    12         Console.WriteLine("起飞");
    13         Console.WriteLine("我飞,我飞,我飞飞飞");
    14     }
    15 }
    复制代码

    再看老虎和苍蝇的实现:

    复制代码
     1 public class 老虎
     2 {
     3     public virtual void 自我介绍()
     4     {
     5         Console.WriteLine("大家好,我是老虎。");
     6     }
     7 }
     8 
     9 public class 苍蝇 : I飞
    10 {
    11     public void 飞一个看看()
    12     {
    13         this.飞();
    14     }
    15 }
    复制代码

    再看超级老虎的实现:

    复制代码
     1 public class 超级老虎 : 老虎, I飞
     2 {
     3     public override void 自我介绍()
     4     {
     5         Console.WriteLine("大家好,我是超级老虎哦!");
     6     }
     7 
     8     public void 我会飞哟()
     9     {
    10         this.飞();
    11     }
    12 }
    复制代码

    怎么样,你看明白了吗?这个实现是不是很简单呢?好处是不是大大的有呢?

    当以后哪天老板让你实现一个会飞的超级猪的话,你只需要让你的超级猪继承 “I飞” 接口就行了。当哪天老板又不想要这个超级猪飞的话,你也只需要将这个接口继承删掉而已。如果你正在开发一个动物王国程序,你可以将飞的功能注入到任何一种动物身上。想想是不是都觉得很爽。

    实战经验

    在实际开发中有没有使用多继承的情况呢?我在这里跟大家分享一个我们项目中大量用到的情景。

    我们的项目是基于ASP.NET MVC4的,下面有多个area,可以理解为独立模块。每个模块都有一个相似的注销登录的功能,于是我们使用了ILogoutController,然后在其扩展方法中实现了Logout的功能。注销登录会做这些事:清空session,保存用户状态,写入日志等。这样,只要某个controller继承自ILogoutController,那么这个controller就拥有了注销登录的功能。

    原理上讲,只要是功能性的相似,我们都可以用空接口扩展的方法来写多继承,从而实现功能注入,而注入式的代码也更容易维护。

    总结

    最后,再让我们回顾一下之前用C++写的超级老虎吃屎的变态例子。这实际上不是C++的错,而是程序员用错了多继承。虽然在语法上C++没有限制程序员怎么去写多继承,但是从上面的例子分析来看,我们很容得出这样一个结论:

    当需要写多继承的时候,被继承的父类只能是一个功能,而不应是一个完整的类。

    如果按照这个思路,那么今天的这个例子在C++中就可以这样写,首先提一个Flyable的类出来,然后让超级老虎和苍蝇都继承这个Flyable。

    在C#中,虽然实现多继承的代码稍微绕了个弯,但是多继承带来的好处是非常明显的:对不同的类实现注入式的功能,让你的代码更符合面向对象的思想。


  • 相关阅读:
    实习工作两月
    远程mysql_java.sql.SQLException: null, message from server: "Host 'xxx' is not allowed to connect
    大学毕业后拉开差距的真正原因写给将要毕业的自己
    var nameValue=$("#"+name+"DelFlag_"+id).attr("name"); 中的nameValue是一个字符串!并非boolean值
    装饰模式
    数据结构图(非带权图)(js)
    WPF的逻辑树与视觉树(1)基本概念
    WPF的动画(2)Animation
    集合附加属性(HACK)
    WPF的逻辑树与视觉树(2)Visual容器
  • 原文地址:https://www.cnblogs.com/kevinWu7/p/10163544.html
Copyright © 2020-2023  润新知