【问题标题】:C# What is the advantage of using interfaces for simulating multiple inheritance? [closed]C#使用接口模拟多重继承有什么好处? [关闭]
【发布时间】:2017-03-27 10:59:06
【问题描述】:

接口是 C# 的一个特性,我从来没有完全理解它的用途。我看到它们一直用在专业代码中,但在我的一生中,我无法弄清楚其中的原因。

我的问题是关于模拟多重继承的接口。在阅读这个主题时,我经常会遇到这样的例子:

interface IAddition
{
    int add(int a, int b);
}

interface ISubtraction
{
    int sub(int a, int b);
}

class Calculation : IAddition, ISubtraction
{        
    public int add(int a, int b)
    {
        return result1 = a + b;
    }

    public int sub(int a, int b)
    {
        return result1 = a - b;
    }
}

我不明白的是 - 接口在这里带来了什么好处?在我看来,您可以完全删除它们,并且 Calculation 类仍然可以完全一样地工作。我在这里想念什么?

任何帮助将不胜感激 - 很长一段时间以来,我一直在寻找能让界面“点击”的东西。

值得注意的是,我是一名游戏开发人员,我正在为此寻找解决方案以提高我开发游戏的能力,但我认为这是一个足够笼统的问题,可以在这里发布。

【问题讨论】:

  • 对我来说,这看起来像是一个相当糟糕的例子。我建议去寻找更好的教程或简单地研究 .NET 框架中的 Interfecs。不过,作为一个 SO 问题,它不再是主题。
  • 查看框架接口,例如public class HashSet<T> : ICollection<T>, IEnumerable<T>, IEnumerable, ISerializable, IDeserializationCallback, ISet<T>, IReadOnlyCollection<T>。通常一个集合会实现多个。

标签: c# interface multiple-inheritance


【解决方案1】:

See this msdn article for details ("No Multiple-Inheritance in .NET" section):

接口解决了多重继承的问题,当编译器试图找到正确的实现时,不允许这样做以防止编译器出现歧义。在他们的示例中,BabyBasset 继承了 Hound 和 Puppy,但假设这两个类都提供了一个名为“Bark()”的方法的实现,哪一个会被执行?因此模棱两可。

类可以实现多个接口,如果存在歧义(即在不同接口中具有相同名称的两个方法),它们可以在类中显式实现,从而消除歧义。

【讨论】:

  • “接口解决多重继承问题” - 只是接口是什么/为什么是的一小部分。
  • 我完全同意,@HenkHolterman,但从问题中可以清楚地看出,提问者理解接口的基本概念,但很难将它们的用途与类继承区分开来。
【解决方案2】:

接口是对象将具有提供特定功能的某些成员的契约。

这允许您提供一定程度的抽象,以便编写代码以使用接口而不是类。

例如,我可能有一个继承 IList 接口的类。可能有许多简单地存储“事物”列表的实现。在幕后,实现是什么对我来说并不重要,我可能关心的是它们包括Add()GetEnumerator(),所以我可以添加东西并循环它们。由继承接口的类的实现者来提供实现。

通常会进行多重继承,以便您可以拆分功能。你可能有一个做减法、加法或两者兼而有之的课程。调用继承添加接口的对象上的方法的代码不关心提供添加功能的成员以外的任何内容。与使用继承减法接口的类的代码相同。然后,它允许您或其他人用其他东西替换这些类,只要它们实现了消费者不关心的接口。

【讨论】:

    【解决方案3】:

    接口基本上是您和您的客户之间的合同,就像强制执行一样,它强制您的客户必须实现接口中声明的方法。

    让我们以你的代码为例。

    interface IAddition
    {
        int add(int a, int b);
    }
    
    interface ISubtraction
    {
        int sub(int a, int b);
    }
    
    class Calculation : IAddition, ISubtraction
    {
    
    }
    

    所以如果你不实现AddSub 方法,编译器会给你错误。如果你删除你的接口,就没有合同,所以没有人会检查你的类实现。你做任何你想做的事。

    允许多重继承,在多种场景下都有很好的意义。你的类可以与其他接口有多个合同。示例您有 1 个接口,10 个不同的接口正在实现此接口。所以有些类可以实现最顶层的接口,有些可以实现 1,2,5 个接口,具体取决于它们各自的功能。

    【讨论】:

      【解决方案4】:

      接口是一种绑定契约,可确保您的类实现某些功能。一个类使用的接口越多,宣传使用的特性就越多。

      想想像麦片盒标签这样的界面。如果它说“不加糖”,你可以相当肯定你的麦片(或你的班级)没有加糖。

      在您的示例中,IAddISub 正在宣传您的班级知道如何执行某些操作。由你决定你希望你的类的特性在一个或多个接口中拆分到什么细粒度级别。这一切都取决于未来的使用。如果你有一个只被一个类使用的接口,那么也许这个接口并不是真正需要的。

      【讨论】:

        【解决方案5】:

        是的,你是对的。删除接口后,代码仍将编译。那么为什么要添加它们呢?

        好吧,您的IAdditionISubtraction 示例并不能很好地说明我的观点。既然学习编程最好的方式就是看别人是怎么写代码的,我就以System.IComparable<T>接口为例。它是.NET框架提供的接口。

        我们之所以使用接口,是为了实现多态。接口就像类必须遵守的协议。如果一个类实现了一个接口,则保证该类可以做接口指定的事情。这一切听起来很混乱,所以让我们看一下IComparable<T> 接口。为了让事情更简单,让我们把它改成IComparable<int>

        IComparable<int>有这个方法:

        int CompareTo(int other);
        

        这基本上意味着

        所有实现IComparable<int>的东西都有能力与整数进行比较。我们可以调用它的CompareTo方法来判断它是大于、等于还是小于任何int

        由于实现IComparable<int> 的所有东西都必须具有CompareTo 方法,因此可以保证您调用CompareTo 的对象可以与int 进行比较。

        那么这有什么用呢?

        当您对数组进行排序时,与其他事物进行比较的能力很有用。 Array.Sort 方法使用IComparable<T> 接口。以下是Sort 方法的工作原理(高度简化):

        它首先检查数组的元素是否可以比较,检查它们是否实现IComparable<T>。如果有,请致电CompareTo 进行比较!为什么可以这么肯定有一个方法叫CompareTo?因为对象都实现了IComparable<T>,这就保证了CompareTo方法!比较之后,Sort 可以判断出哪个元素在先和最后。

        您知道不同的事物需要进行不同的比较吗?整数和doubles 可以通过将一个相减并检查结果是正数还是负数来进行比较。但是字符串是按字母顺序比较的。如果没有IComparable<T> 接口,那么对于字符串将有一个不同的Sort 方法,对于int 有一个Sort 方法,对于所有可以比较的类型都有一个Sort 方法。更糟糕的是,如果客户端代码创建了可以比较的东西,客户端代码需要编写自己的Sort 方法!不要忘记,还有很多其他方法可以利用比较的能力。是否所有那些方法都需要为每种类型提供不同的版本?

        这就是接口如此重要的原因。有了它们,只需要一个Sort 方法,因为多态会处理其余的事情。

        让我总结一下接口的优点:

        • 它提供了灵活性(整数和字符串都可以与整数和字符串进行比较,但方式不同)
        • 它减少了代码(您不需要编写额外的Sort 方法!)
        • 它确保安全(如果你忘记实现一个方法,编译器会告诉你!)

        我们假设IComparable<T>不存在,而stringint等类型可以比较为自己的CompareTo方法。 Sort 方法看起来像这样

        public static void Sort(int[] arr) {
            // note that I'm using bubble sort here. A real sort method wouldn't use this coz it's slow.
            // source: http://stackoverflow.com/questions/14768010/simple-bubble-sort-c-sharp
            int temp = 0;
        
            for (int write = 0; write < arr.Length; write++) {
                for (int sort = 0; sort < arr.Length - 1; sort++) {
                    if (arr[sort].CompareTo(arr[sort + 1]) > 0) {
                        temp = arr[sort + 1];
                        arr[sort + 1] = arr[sort];
                        arr[sort] = temp;
                    }
                }
            }
        
        }
        
        public static void Sort(string[] arr) {
            string temp = 0;
        
            for (int write = 0; write < arr.Length; write++) {
                for (int sort = 0; sort < arr.Length - 1; sort++) {
                    if (arr[sort].CompareTo(arr[sort + 1]) > 0) {
                        temp = arr[sort + 1];
                        arr[sort + 1] = arr[sort];
                        arr[sort] = temp;
                    }
                }
            }
        
        }
        

        您可能会争辩说,一个采用object[] 检查每个元素类型的Sort 方法也可以工作。是的,但这与多个Sort 方法存在相同的问题。如果创建了新的可比较类,您仍然需要添加另一个排序方法或另一个检查。

        如果你有IComparable&lt;T&gt;接口,问题就全部结束了!

        public static void Sort<T>(T[] arr) where T : IComparable<T> {
            T temp;
        
            for (int write = 0; write < arr.Length; write++) {
                for (int sort = 0; sort < arr.Length - 1; sort++) {
                    if (arr[sort].CompareTo(arr[sort + 1]) > 0) {
                        temp = arr[sort + 1];
                        arr[sort + 1] = arr[sort];
                        arr[sort] = temp;
                    }
                }
            }
        }
        

        由于类型约束表明T 必须实现IComparable&lt;T&gt;,因此可以毫无问题地对数组中的每个对象调用CompareTo

        现在,如果您想添加另一个可比较的类,您需要做的就是实现接口。而如果接口不存在,则必须编写一个CompareTo 方法一个Sort 方法!

        【讨论】:

        • 这是一个非常好的答案,非常有帮助。我开始琢磨它了!但是,我不太确定我是否了解它如何减少代码。如果您实际上不能在接口中编写任何方法的实现,如何减少代码?难道你还不需要编写各种排序方法吗?感谢您迄今为止的帮助。
        • 哦,哎呀!我的意思是“重复代码”。使用接口,您只需要为每个可以比较的类提供一个 CompareTo 方法,这比您在对数组进行排序时可以调用的 10 多个 Sort 方法要好得多。 @Infergnome
        • 谢谢。我想我正在经历一个心理障碍。您能否提供一些sn-p 代码来展示Sort 的不同实现? (一个有接口,一个没有)?那会很有帮助。
        • @Infergnome 已编辑
        • 非常感谢!我的心理障碍已被@Sweeper 清除了! (请原谅双关语)
        猜你喜欢
        • 2012-01-21
        • 2013-08-05
        • 2018-02-14
        • 1970-01-01
        • 2020-06-08
        • 2013-03-31
        • 2011-03-02
        • 2011-04-29
        • 2012-10-11
        相关资源
        最近更新 更多