当前位置:  开发笔记 > 编程语言 > 正文

C#应该有多重继承吗?

如何解决《C#应该有多重继承吗?》经验,为你挑选了9个好方法。

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

多重继承过于复杂,而且往往含糊不清

这是不必要的,因为接口提供类似的东西

如果接口不合适,组合是一个很好的替代品

我来自C++背景,错过了多重继承的力量和优雅.虽然它不适合所有软件设计,但在某些情况下很难否认它在接口,组合和类似的OO技术上的实用性.

是否排除了多重继承,说开发人员不够聪明,不能明智地使用它们,并且在它们出现时无法解决这些复杂问题?

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


附录:我很想知道来自单一(或程序背景)与多重继承背景的回复.我经常发现,没有多重继承经验的开发人员通常会默认使用多继承是不必要的参数,因为他们没有任何关于范例的经验.



1> Marc Gravell..:

我从来没有错过过一次,也从未错过过.是的,它[MI]变得复杂,是的,接口在很多方面做了类似的工作 - 但这不是最重要的一点:在一般意义上,大多数时候根本不需要它.在许多情况下,甚至单一继承也被过度使用.


如果你拿走物品,我会非常想念他们; 这就是区别.对象以最小的复杂性增加了巨大价值 多重继承在大规模复杂性上增加(可以说)最小值.
严格来说(我只是在这里解释Stroustrup),这是真的,而且确实不需要单继承.此外,课程也不是真的需要,对吗?它不是"需要"而是"很高兴"
在某些情况下,任何单一功能都会有所帮助.这是否意味着语言应该是一个厨房水槽,你能想到的每一个可能的功能?我不相信多重继承增加的复杂性负担是值得的.
好吧,让我重新说一句; 从我作为.NET开发人员的盲目观点来看,/使用objects /很简单.我知道我会很快失败.但是,是的,点了 - 对象增加了复杂性.这是关于复杂性与奖励的关系; 对象有很大的好处; MI有一些好处,但IMO不够.
MI很简单,但是如果你不能相信自己不要创建一个类似树的大规模层次结构,那么我认为你根本不应该编程.我认为C#设计师只是想让自己的生活变得轻松,MI对于编译器来说很难.
就个人而言,我宁愿找到一个不需要我(仅仅是凡人)的设计来理解*一个巨大的树状层次结构.

2> Chris Cudmor..:

更喜欢继承聚合!

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的不同实现来创建应用程序的多个版本,而无需编写另一个类.

依赖注入可以帮助解决这个问题.


你可以在这里使用mixin,而不是手动编写组合:-)
在示例中将"aggregation"替换为"composition".如果您来自多重继承,则需要编写的超类中的所有类.以下是`aggregation`和`composition`之间的区别:http://en.wikipedia.org/wiki/Object_composition#Aggregation

3> Mark Cidade..:

处理多重继承的一个问题是接口继承和实现继承之间的区别.

通过使用纯接口,C#已经有了一个干净的接口继承实现(包括隐式或显式实现的选择).

如果你看一下C++,对于每一个类,你在冒号后指定class申报,继承时获得的是由访问修饰符确定(private,protected,或public).通过public继承,您可以获得多重继承的完全混乱 - 多个接口与多个实现混合在一起.通过private继承,您只需实现." class Foo : private Bar" 的对象永远不会传递给期望a的函数,Bar因为它就好像Foo该类实际上只有一个私有Bar字段和一个自动实现的委托模式.

纯多重实现继承(实际上只是自动委托)不存在任何问题,并且在C#中很棒.

对于类的多接口继承,有许多不同的可能设计来实现该功能.每个具有多重继承的语言都有自己的规则,以确定在多个基类中使用相同名称调用方法时会发生什么.有些语言,如Common Lisp(特别是CLOS对象系统)和Python,有一个元对象协议,您可以在其中指定基类优先级.

这是一种可能性:

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)

在这种情况下,在冲突的情况下,只隐式继承第一个列出的类的实现.必须明确指定其他基类型的类才能获得它们的实现.为了使它更具有傻瓜性,编译器可以在冲突的情况下禁止隐式继承(冲突方法总是需要强制转换).

此外,您可以使用隐式转换运算符在C#中实现多重继承:

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; 
    }
}

它并不完美,不过,因为它不是由支持isas运营商,以及System.Type.IsSubClassOf().


+++对我来说就是这个.Eiffel和Sather是正确完成多重继承的好例子.www.icsi.berkeley.edu /〜sather /
在C++中很好地使用多重继承是实现mixins的一种方式(http://en.wikipedia.org/wiki/Mixin).我认为对mixins(或私有实现继承)的本机C#支持将是一个有用的功能.

4> Lou Franco..:

这是我一直遇到的多重继承的一个非常有用的案例.

作为工具包供应商,我无法更改已发布的API,或者我将破坏向后兼容性.由此产生的一件事是,一旦我发布它就不能添加到接口,因为它会破坏实现它的任何人的编译 - 唯一的选择是扩展接口.

这对现有客户来说很好,但新的会看到这个层次结构不必要复杂,如果我从一开始就设计它,我不会选择以这种方式实现它 - 我必须,否则我会失去向后兼容性.如果接口是内部的,那么我只需添加它并修复实现者.

在许多情况下,接口的新方法有一个明显的小默认实现,但我不能提供它.

我更喜欢使用抽象类,然后当我必须添加一个方法时,添加一个带有默认实现的虚拟方法,有时我们会这样做.

当然,问题在于,如果这个类可能会混合到已经扩展的东西中 - 那么我们别无选择,只能使用接口并处理扩展接口.

如果我们认为我们有很大的问题,我们选择一个丰富的事件模型 - 我认为这可能是C#中的正确答案,但不是每个问题都以这种方式解决 - 有时候你需要一个简单的公共接口对于扩展者来说,还有一个更丰富的.



5> David Arno..:

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


类似,但不一样.我还认为扩展方法伴随着它们自身的复杂性.我认为CodingHorror有一个帖子.

6> Qwertie..:

我不知道CLR是否支持多重继承,因此我怀疑它是否能够以有效的方式得到支持,就像在C++中一样(或者Eiffel,考虑到语言是专门设计的,它可以做得更好)为MI).

多重继承的一个很好的替代方法叫做Traits.它允许您将各种行为单元混合到一个类中.编译器可以支持traits作为单继承类型系统的编译时扩展.您只需声明类X包含特征A,B和C,并且编译器将您要求的特征放在一起以形成X的实现.

例如,假设您正在尝试实现IList(T).如果你看一下IList(T)的不同实现,它们通常会共享一些完全相同的代码.这就是特征.你只需要在其中声明一个包含公共代码的特征,并且可以在IList(T)的任何实现中使用该公共代码 - 即使实现已经有其他基类.这是语法的样子:

/// This trait declares default methods of IList
public trait DefaultListMethods : IList
{
    // 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 comparer = EqualityComparer.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 GetEnumerator()
    {
        for (int i = 0; i < Count; i++)
            yield return this[i];
    }
}

你使用这样的特征:

class MyList : MyBaseClass, DefaultListMethods
{
    public void Insert(int index, T item) { ... }
    public void RemoveAt(int index)       { ... }
    public T this[int index] {
        get { ... }
        set { ... }
    }
    public int Count {
        get { ... }
    }
}

当然,我只是在这里摸索表面.有关更完整的描述,请参阅论文特征:可组合行为单位(PDF).

Rust语言(来自Mozilla)以一种有趣的方式实现了Traits:他们注意到traits类似于默认接口实现,因此他们将接口和traits统一为单个功能(他们称之为traits).traits和默认接口实现(Java现在拥有)之间的主要区别在于traits可以包含private或protected方法,这与传统的必须公开的接口方法不同.如果特征和接口没有统一到一个特征中,那么另一个区别是你可以引用一个接口,但你不能引用一个特征; 特质本身不是一种类型.



7> Brian Genisi..:

实际上,由于某个特定原因,我错过了多重继承......处理模式.

每次我需要实现dispose模式时,我都会对自己说:"我希望我可以从一个实现带有一些虚拟覆盖的dispose模式的类派生出来." 我将相同的样板代码复制并粘贴到每个实现IDispose的类中,我讨厌它.


实际上,我意识到我不需要多重继承...我真正需要的是混合.这样我就可以在不需要多重继承的情况下引入Dispose模式代码.

8> Kibbee..:

我只是因为你陈述的原因而反对多重继承.开发人员会滥用它.我已经看到从实用程序类继承的每个类都有足够的问题,因此你可以从每个类调用一个函数而不需要输入那么多,知道多重继承会在很多情况下导致错误的代码.关于GoTo可以说同样的事情,这是它使用的原因之一是如此不受欢迎.我认为多重继承确实有一些很好的用途,就像GoTo一样.在一个理想的世界里,只有在恰当的时候使用它们才会有问题.然而,世界并不理想,所以我们必须保护坏程序员.


为了保证开发人员的安全,削弱语言并不是一个好的设计原则.这是Java所具有的问题(除了类和接口之外什么都不容易理解......).
阅读Stroustrup的"设计与进化"一书.他对此持不妥协的态度:因为它会被误用,所以不会被语言排除在外.他的目标是有能力的程序员(坦率地说,我不希望无能为力的程序员编写程序).

9> Thomas Hanse..:

是!是!是的!

说真的,我整个职业生涯都在开发GUI库,MI(多重继承)使这个FAR比SI(单继承)更容易

首先我用C++ 做了SmartWin ++(MI大量使用),然后我做了Gaia Ajax,最后做了Ra-Ajax,我可以非常自信地说MI管理某些地方.其中一个地方是GUI库......

并且声称MI"过于复杂"的论点大多是由那些试图构建语言战争并且恰好属于"目前没有MI"的阵营的......

就像函数式编程语言(如Lisp)已经被非函数式编程语言倡导者教授("非Lispers")一样"过于复杂"......

人们害怕未知......

MI规则!

推荐阅读
手机用户2402852387
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有