C#应该有多重继承吗?

C#应该有多重继承吗?,c#,inheritance,multiple-inheritance,C#,Inheritance,Multiple Inheritance,我遇到过许多反对在C#中包含多重继承的论点,其中一些包括(哲学论点除外): 多重继承太复杂,而且常常模棱两可 这是不必要的,因为接口提供了类似的功能 当接口不合适时,组合是一个很好的替代品 我来自C++背景,错过了多重继承的力量和优雅。虽然它不适合所有的软件设计,但在某些情况下,很难否认它在接口、组合和类似OO技术上的实用性 排除多重继承是否意味着开发人员不够聪明,无法明智地使用它们,并且无法解决出现的复杂性 我个人很欢迎将多重继承引入C#(也许是C#) 附录:我想从回答中了解哪些人来自单

我遇到过许多反对在C#中包含多重继承的论点,其中一些包括(哲学论点除外):

  • 多重继承太复杂,而且常常模棱两可
  • 这是不必要的,因为接口提供了类似的功能
  • 当接口不合适时,组合是一个很好的替代品
<>我来自C++背景,错过了多重继承的力量和优雅。虽然它不适合所有的软件设计,但在某些情况下,很难否认它在接口、组合和类似OO技术上的实用性

排除多重继承是否意味着开发人员不够聪明,无法明智地使用它们,并且无法解决出现的复杂性

我个人很欢迎将多重继承引入C#(也许是C#)



附录:我想从回答中了解哪些人来自单一(或程序背景)或多重继承背景。我经常发现,没有多重继承经验的开发人员通常会默认多重继承是不必要的论点,因为他们没有任何范例经验。

我从来没有错过过一次,从来没有。是的,它(MI)变得复杂,是的,接口在许多方面做着类似的工作——但这不是最重要的一点:在一般意义上,大多数时候根本不需要它。在许多情况下,即使是单一继承也会被过度使用。

我认为如果没有提供足够的ROI,它会使事情变得过于复杂。我们已经看到人们用太深的继承树来屠杀.NET代码。我可以想象,如果人们有权继承多重遗产,会发生什么样的暴行


我不否认它有潜力,但我只是没有看到足够的好处。

C#支持单一继承、接口和扩展方法。在它们之间,它们提供了多重继承所提供的一切,而没有多重继承所带来的麻烦。

我反对多重继承的理由仅仅是你所说的。开发者会滥用它。我已经看到了从实用程序类继承的每个类的足够多的问题,只是为了让您可以从每个类调用函数,而不需要太多的键入,知道多重继承在许多情况下都会导致错误代码。关于GoTo也可以说同样的话,这也是人们对它的使用如此不满的原因之一。我认为多重继承确实有一些很好的用途,就像GoTo一样,在一个理想的世界中,它们只有在适当的时候才被使用,不会有任何问题。然而,这个世界并不理想,所以我们必须保护糟糕的程序员不受他们自己的影响。

虽然在某些情况下它确实是有用的,但我发现大多数时候,当我认为我需要它时,我真的不知道。

自从C#首次作为alpha/beta版本发布以来,我就一直在使用它,而且从未错过过多重继承。MI在某些方面很好,但几乎总是有其他方法可以达到相同的结果(其中一些方法实际上更简单或创建更易于理解的实现)。

更喜欢聚合而不是继承

class foo : bar, baz
通常处理得更好

class foo : Ibarrable, Ibazzable
{
  ... 
  public Bar TheBar{ set }
  public Baz TheBaz{ set }

  public void BarFunction()
  {
     TheBar.doSomething();
  }
  public Thing BazFunction( object param )
  {
    return TheBaz.doSomethingComplex(param);
  }
}
通过这种方式,您可以交换IBarrable和IBazzable的不同实现,以创建应用程序的多个版本,而无需编写另一个类


依赖注入对此有很大帮助。

多重继承通常是有用的,许多OO语言以某种方式实现它(C++、Eiffel、CLOS、Python……)。这是必要的吗?不,有这么好吗?是。

更新
我向每一个投票反对我的人提出挑战,让他们向我展示任何多重继承的例子,我不能轻易地将其移植到具有单一继承的语言。除非有人能出示任何这样的样品,否则我声称它是不存在的。我已经将大量的C++代码(MH)移植到java(没有MH),这从来都不是问题,不管C++代码使用了多少MH。
到目前为止,没有人能够证明多重继承比您在文章中提到的其他技术更有效(使用接口和委托,我可以在没有太多代码或开销的情况下获得完全相同的结果),但它有几个众所周知的缺点(最令人讨厌的缺点)

实际上,多重继承通常被滥用。如果您使用OO设计以某种方式将真实世界建模为类,您将永远无法达到多重继承真正有意义的程度。你能为多重继承提供一个有用的例子吗?到目前为止,我看到的大多数例子实际上都是“错误的”。它们使某些东西成为子类,实际上只是一个额外的属性,因此实际上是一个接口


看一看。它是一种编程语言,接口确实有多重继承,为什么不呢(它不会产生菱形问题),但是没有接口的类没有继承。它们只能实现接口,并且可以“包含”其他对象,这使这些其他对象成为它们的固定部分,但这与继承不同,它是一种委托形式(通过包含对象而“继承”的方法调用实际上只是转发到封装在对象中的这些对象的实例)。我认为这个概念非常有趣,它表明你可以拥有一个完全干净的OO语言,而不需要任何实现继承。

一位同事写了这个博客,讲述了如何通过动态编译在C#中获得类似多重继承的东西:

否。


(投票)

我认为这很简单。就像任何其他复杂的编程范例一样,您可能会误用它并伤害自己。你可以误用对象(哦,是的!),但这并不意味着OO本身就是坏的

/// This trait declares default methods of IList<T>
public trait DefaultListMethods<T> : IList<T>
{
    // Methods without bodies must be implemented by another 
    // trait or by the class
    public void Insert(int index, T item);
    public void RemoveAt(int index);
    public T this[int index] { get; set; }
    public int Count { get; }

    public int IndexOf(T item)
    {
        EqualityComparer<T> comparer = EqualityComparer<T>.Default;
        for (int i = 0; i < Count; i++)
            if (comparer.Equals(this[i], item))
                return i;
        return -1;
    }
    public void Add(T item)
    {
        Insert(Count, item);
    }
    public void Clear()
    {   // Note: the class would be allowed to override the trait 
        // with a better implementation, or select an 
        // implementation from a different trait.
        for (int i = Count - 1; i >= 0; i--)
            RemoveAt(i);
    }
    public bool Contains(T item)
    {
        return IndexOf(item) != -1;
    }
    public void CopyTo(T[] array, int arrayIndex)
    {
        foreach (T item in this)
            array[arrayIndex++] = item;
    }
    public bool IsReadOnly
    {
        get { return false; }
    }
    public bool Remove(T item)
    {
        int i = IndexOf(item);
        if (i == -1)
            return false;
        RemoveAt(i);
        return true;
    }
    System.Collections.IEnumerator 
        System.Collections.IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
    IEnumerator<T> GetEnumerator()
    {
        for (int i = 0; i < Count; i++)
            yield return this[i];
    }
}
class MyList<T> : MyBaseClass, DefaultListMethods<T>
{
    public void Insert(int index, T item) { ... }
    public void RemoveAt(int index)       { ... }
    public T this[int index] {
        get { ... }
        set { ... }
    }
    public int Count {
        get { ... }
    }
}
abstract class Gun
{ 
    public void Shoot(object target) {} 
    public void Shoot() {}

    public abstract void Reload();

    public void Cock() { Console.Write("Gun cocked."); }
}

class Camera
{ 
    public void Shoot(object subject) {}

    public virtual void Reload() {}

    public virtual void Focus() {}
}

//this is great for taking pictures of targets!
class PhotoPistol : Gun, Camera
{ 
    public override void Reload() { Console.Write("Gun reloaded."); }

    public override void Camera.Reload() { Console.Write("Camera reloaded."); }

    public override void Focus() {}
}

var    pp      = new PhotoPistol();
Gun    gun     = pp;
Camera camera  = pp;

pp.Shoot();                    //Gun.Shoot()
pp.Reload();                   //writes "Gun reloaded"
camera.Reload();               //writes "Camera reloaded"
pp.Cock();                     //writes "Gun cocked."
camera.Cock();                 //error: Camera.Cock() not found
((PhotoPistol) camera).Cock(); //writes "Gun cocked."
camera.Shoot();                //error:  Camera.Shoot() not found
((PhotoPistol) camera).Shoot();//Gun.Shoot()
pp.Shoot(target);              //Gun.Shoot(target)
camera.Shoot(target);          //Camera.Shoot(target)
public class PhotoPistol : Gun /* ,Camera */
{
    PhotoPistolCamera camera;

    public PhotoPistol() {
        camera = new PhotoPistolCamera();
    }

    public void Focus() { camera.Focus(); }

    class PhotoPistolCamera : Camera 
    { 
        public override Focus() { }
    }

    public static Camera implicit operator(PhotoPistol p) 
    { 
        return p.camera; 
    }
}
interface I
{
    void F();
    void G();
}


class DefaultI : I
{
    void F() { ... }
    void G() { ... }
}

class C : I = DefaultI
{
    public void F() { ... } // implements I.F
}