使用接口模拟多重继承的优点是什么?

本文关键字:是什么 多重继承 接口 模拟 | 更新日期: 2023-09-27 18:17:39

接口是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;
    }
}

我不明白的是——接口带来了什么好处?在我看来,您可以完全删除它们,而计算类仍然可以完全相同地工作。我遗漏了什么?

任何帮助都将是感激的-我一直在寻找一些东西,使界面"点击"在我的脑海中已经有一段时间了。

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

使用接口模拟多重继承的优点是什么?

你说得对。删除接口后,代码仍然可以编译。那么为什么要添加它们呢?

嗯,你的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可以计算出哪个元素是第一个和最后一个。

你知道不同的事物需要用不同的方式进行比较吗?整数和double s可以通过两者相减,并检查结果是正数还是负数来进行比较。但是字符串是按字母顺序比较的。如果没有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;
            }
        }
    }
}

你可以认为一个Sort方法接受一个object[]来检查每个元素的类型也可以工作。是的,但这与多个Sort方法存在相同的问题。您仍然需要添加另一个排序方法,或者如果创建了一个新的可比类,则需要添加另一个检查。

如果你有一个IComparable<T>接口,问题就结束了!

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<T>,所以可以在数组中的每个对象上调用CompareTo而没有问题!

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

详细信息请参阅msdn文章(" .NET中没有多重继承"一节):

Interfaces解决了多重继承的问题,当编译器试图找到正确的实现时,多重继承是不允许防止歧义的。在他们的例子中,BabyBasset继承了Hound和Puppy,但是假设这两个类都提供了一个名为"Bark()"的方法的实现,那么哪个会被执行呢?这就是歧义。

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

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

这允许您提供一个抽象级别,以便代码可以编写为与接口而不是类一起工作。

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

使用多重继承通常是为了拆分功能。你可能有一个做减法、加法或两者都做的类。调用继承了添加接口的对象上的方法的代码只关心提供添加功能的成员。使用继承减法接口的类的代码也是如此。然后,它允许您或其他人用其他东西替换这些类,只要它们实现了接口,消费者就不会关心。

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

让我们以你的代码为例。

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

所以如果你不实现AddSub方法,编译器会给你错误。如果你删除了你的接口,就没有契约了,所以没有人会检查你的类实现。你想怎么做就怎么做。

允许

多重继承,它在多个场景中具有很好的意义。类可以与其他接口有多个契约。例如你有一个接口,10个不同的接口都在实现这个接口。有些类可以实现最上面的接口,有些可以实现1 2 5个接口,这取决于它们各自的功能。

接口是一个绑定契约,它保证你的类将实现一些特性。类使用的接口越多,宣传使用的特性就越多。

把接口想象成麦片包装盒的标签。如果上面写着"无添加糖",你就可以相当肯定你的麦片(或你的课)没有添加任何糖。

在您的示例中,IAddISub正在宣传您的类知道如何执行某些操作。您可以决定将类的特性划分为一个或多个接口的细粒度级别。这完全取决于未来的使用情况。如果你有一个接口只被一个类使用,那么也许这个接口并不是真正需要的。