Programing

C #에는 다중 상속이 있어야합니까?

lottogame 2021. 1. 9. 09:15
반응형

C #에는 다중 상속이 있어야합니까?


나는 C #에 다중 상속을 포함하는 것에 반대하는 수많은 주장을 보았습니다. 그 중 일부는 (철학적 주장은 제쳐두고) 다음과 같습니다.

  • 다중 상속이 너무 복잡하고 종종 모호합니다.
  • 인터페이스가 비슷한 것을 제공하기 때문에 불필요합니다.
  • 구성은 인터페이스가 부적절한 경우 좋은 대체물입니다.

저는 C ++ 배경에서 왔고 다중 상속의 힘과 우아함을 놓칩니다. 모든 소프트웨어 설계에 적합하지는 않지만 인터페이스, 구성 및 유사한 OO 기술에 대한 유용성을 부정하기 어려운 상황이 있습니다.

다중 상속을 배제하면 개발자가 현명하게 사용할만큼 똑똑하지 않고 복잡성이 발생할 때이를 해결할 수 없다는 의미입니까?

개인적으로 C # (아마도 C ##)에 다중 상속을 도입하는 것을 환영합니다.


부록 : 단일 (또는 절차 적 배경) 대 다중 상속 배경에서 오는 응답을 통해 알고 싶습니다. 나는 종종 다중 상속에 대한 경험이없는 개발자가 단순히 패러다임에 대한 경험이 없기 때문에 종종 다중 상속이 불필요하다는 주장을 기본으로한다는 것을 발견했습니다.


나는 그것을 한 번도 놓친 적이 없습니다. 예, 그것은 복잡해집니다. 그리고 예, 인터페이스는 여러면에서 유사한 작업을 수행합니다.하지만 이것이 가장 큰 요점은 아닙니다. 일반적으로 대부분의 경우 단순히 필요하지 않습니다. 단일 상속조차도 많은 경우에 남용됩니다.


상속보다 집계를 선호하십시오!

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의 다양한 구현을 교체하여 다른 클래스를 작성하지 않고도 앱의 여러 버전을 만들 수 있습니다.

의존성 주입은 이것에 많은 도움이 될 수 있습니다.


다중 상속을 처리 할 때 발생하는 문제 중 하나는 인터페이스 상속과 구현 상속의 차이입니다.

C #은 이미 순수 인터페이스를 사용하여 인터페이스 상속 (암시 적 또는 명시 적 구현 선택 포함)을 깔끔하게 구현했습니다.

당신이 C 보면 ++, 각 클래스에 대해 당신이에 콜론 뒤에 지정 class선언, 당신이 얻을 상속의 종류는 액세스 한정자에 의해 결정된다 ( private, protected또는 public). public상속을 사용하면 다중 상속의 완전한 혼란을 얻을 수 있습니다. 다중 인터페이스는 다중 구현과 혼합됩니다. 함께 private상속, 당신은 단지 구현을 얻는다. " class Foo : private Bar" 객체는 클래스가 실제로 비공개 필드와 자동으로 구현 된 위임 패턴Bar있는 것처럼를 기대하는 함수로 전달 될 수 없습니다 .FooBar

순수 다중 구현 상속 (실제로는 자동 위임)은 문제를 일으키지 않으며 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().


다음은 내가 항상 겪는 다중 상속에 대한 매우 유용한 사례입니다.

툴킷 공급 업체로서 게시 된 API를 변경할 수 없거나 이전 버전과의 호환성을 깨뜨릴 것입니다. 그 결과 한 가지 결과는 인터페이스를 구현하는 모든 사람을 위해 컴파일을 중단하기 때문에 릴리스 한 후에는 인터페이스에 추가 할 수 없다는 것입니다. 유일한 옵션은 인터페이스를 확장하는 것입니다.

기존 고객에게는 괜찮지 만 새로운 고객은이 계층 구조가 불필요하게 복잡하다고 생각하고 처음부터 설계했다면 이런 방식으로 구현하지 않을 것입니다. 그렇지 않으면 이전 버전과의 호환성을 잃게됩니다. . 인터페이스가 내부 인 경우 추가하고 구현자를 수정합니다.

대부분의 경우 인터페이스에 대한 새로운 메서드는 명확하고 작은 기본 구현을 가지고 있지만 제공 할 수 없습니다.

추상 클래스를 사용하고 나서 메서드를 추가해야 할 때 기본 구현이있는 가상 클래스를 추가하는 것을 선호합니다. 가끔 이렇게합니다.

물론 문제는이 클래스가 이미 무언가를 확장하고있는 것과 혼합 될 가능성이 있다면 인터페이스를 사용하고 확장 인터페이스를 다룰 수밖에 없다는 것입니다.

이 문제가 크게 발생한다고 생각하면 대신 풍부한 이벤트 모델을 선택합니다. C #에서는 정답 ​​일 수 있지만 모든 문제가 이렇게 해결되지는 않습니다. 때로는 간단한 공용 인터페이스가 필요합니다. , 익스텐더의 경우 더 풍부합니다.


C #은 단일 상속, 인터페이스 및 확장 메서드를 지원합니다. 그들 사이에서는 다중 상속이 가져 오는 골칫거리없이 다중 상속이 제공하는 거의 모든 것을 제공합니다.


다중 상속은 내가 ​​아는 어떤 방식 으로든 CLR에서 지원되지 않으므로 C ++ (또는 Eiffel) 에서처럼 효율적인 방식으로 지원 될 수 있을지 의심 스럽습니다. MI).

다중 상속에 대한 좋은 대안은 특성이라고합니다. 다양한 동작 단위를 단일 클래스로 혼합 할 수 있습니다. 컴파일러는 단일 상속 유형 시스템에 대한 컴파일 타임 확장으로 특성을 지원할 수 있습니다. 클래스 X에 트레이 트 A, B 및 C가 포함되어 있다고 선언하면 컴파일러가 요청한 트레이 트를 조합하여 X의 구현을 구성합니다.

예를 들어, IList (of T)를 구현하려고한다고 가정합니다. IList (of T)의 다른 구현을 살펴보면 동일한 코드를 공유하는 경우가 많습니다. 그것은 특성이 들어온 것입니다. 공통 코드가있는 특성을 선언하고 IList (of T)의 모든 구현에서 해당 공통 코드를 사용할 수 있습니다. 구현에 이미 다른 기본 클래스가있는 경우에도 마찬가지입니다. 구문은 다음과 같습니다.

/// 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 { ... }
    }
}

물론, 저는 여기서 표면을 긁고 있습니다. 자세한 설명은 특성 : 구성 가능한 행동 단위 (PDF) 문서를 참조하십시오.

Rust 언어 (Mozilla의)는 흥미로운 방식으로 Traits를 구현했습니다. 트레이 트가 기본 인터페이스 구현과 유사하다는 것을 알아 차리고 인터페이스와 트레이 트를 하나의 기능 (트레이 트라고 부름)으로 통합했습니다. 특성과 기본 인터페이스 구현 (현재 Java에 있음)의 주요 차이점은 특성이 공용이어야하는 기존 인터페이스 메서드와 달리 개인 또는 보호 메서드를 포함 할 수 있다는 것입니다. 특성과 인터페이스가 단일 기능으로 통합 되지 않은 경우 또 다른 차이점은 인터페이스에 대한 참조는 가질 수 있지만 특성에 대한 참조는 가질 수 없다는 것입니다. 특성 자체는 유형이 아닙니다.


나는 실제로 한 가지 특정 이유로 여러 상속을 놓친다.

폐기 패턴을 구현해야 할 때마다 "나는 몇 가지 가상 재정의를 사용하여 폐기 패턴을 구현하는 클래스에서 파생 될 수 있기를 바랍니다."라고 스스로에게 말합니다. IDispose를 구현하는 모든 클래스에 동일한 상용구 코드를 복사하여 붙여 넣는 것이 싫습니다.


나는 단순히 당신이 진술 한 이유 때문에 다중 상속에 반대합니다. 개발자는 그것을 오용 할 것입니다. 유틸리티 클래스에서 상속하는 모든 클래스에 대해 충분한 문제를 보았습니다. 여러 가지 상속이 여러 상황에서 잘못된 코드를 초래할 수 있다는 것을 알기 위해 너무 많이 입력 할 필요없이 모든 클래스에서 함수를 호출 할 수 있도록했습니다. GoTo에 대해서도 똑같은 말을 할 수 있는데, 이것이 사용이 눈살을 찌푸리는 이유 중 하나입니다. 다중 상속은 GoTo처럼 좋은 용도가 있다고 생각합니다. 둘 다 적절할 때만 사용되는 이상적인 세계에서는 문제가 없을 것입니다. 그러나 세상은 이상적이지 않으므로 나쁜 프로그래머를 스스로 보호해야합니다.


예! 예! 그리고 예!

진지하게, 저는 제 경력 내내 GUI 라이브러리를 개발해 왔으며 MI (Multiple Inheritance)는 SI (Single Inheritance)보다이 FAR을 더 쉽게 만듭니다.

먼저 C ++ (MI가 많이 사용됨)에서 SmartWin ++ 를 수행 한 다음 Gaia Ajax를 수행하고 마지막으로 Ra-Ajax를 수행 했으며 MI가 일부 장소에서 규칙을 적용한다는 극도의 확신을 가지고 있습니다. 그중 하나는 GUI 라이브러리입니다 ...

그리고 MI가 "너무 복잡하다"고 주장하는 주장은 대부분 언어 전쟁을 구성하려는 사람들에 의해 제기되며 "현재 MI가없는"캠프에 속하게됩니다.

함수형 프로그래밍 언어 (Lisp와 같은)가 비 기능적 프로그래밍 언어 옹호자들에 의해 "너무 복잡"하다고 가르친 것처럼 ( "비 -Lispers"에 의해) ...

사람들은 미지의 것을 두려워합니다 ...

MI 규칙!


I'm happy that C# does not have Multiple Inheritance, even though it would sometimes be convenient. What I would like to see instead is the ability to provide a default implementation of an interface method. That is:

interface I
{
    void F();
    void G();
}


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

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

In this case, ((I)new C()).F() will call C's implementation of I.F(), while ((I)new C()).G() will call DefaultI's implementation of I.G().

There are a number of issues that the language designers would have to work out before this could be added to the language, but none that are very hard, and the result would cover many of the needs that make Multiple Inheritance desirable.


I have been working with C# since it was first available as an alpha/beta release and have never missed multiple inheritance. MI is nice for some things but there are almost always other ways to achieve the same result (some of which actually end up being simpler or creating an easier to understand implementation).


Multiple inheritance in general can be useful and many OO languages implement it one way or another (C++, Eiffel, CLOS, Python...). Is it essential? No. Is it nice to have? Yes.


Update
I challenge everyone who votes me down to show me any example of multiple inheritance that I can't easily port to a language with single inheritance. Unless anyone can show any such sample, I claim it does not exist. I have ported tons of C++ code (MH) to Java (no-MH) and that was never a problem, no matter how much MH the C++ code used.


Nobody could ever prove so far that multiple inheritance has any advantage over other techniques you mentioned in your post (using interfaces and delegates I can get exactly the same result without much code or overhead), while it has a couple of well known disadvantages (diamond problem being the most annoying ones).

Actually multiple inheritance is usually abused. If you use OO design to somehow model the real world into classes, you will never get to the point where multiple inheritance makes actually sense. Can you provide a useful example for multiple inheritance? Most of the examples I've seen so far are actually "wrong". They make something a subclass, that is in fact just an extra property and thus actually an interface.

Take a look at Sather. It is a programming language, where interfaces do have multiple inheritance, as why not (it can't create a diamond problem), however classes that are no interfaces have no inheritance whatsoever. They can only implement interfaces and they can "include" other objects, which makes these other objects a fixed part of them, but that is not the same as inheritance, it's rather a form of delegation (method calls "inherited" by including objects are in fact just forwarded to instances of these objects encapsulated in your object). I think this concept is pretty interesting and it shows you can have a complete clean OO language without any implementation inheritance at all.


No.

(for voting)


one of the truly nice and (at the time) novel things about the DataFlex 4GL v3+ (I know, I know, Data what?) was its support for mixin inheritance - the methods from any other classes could be reused in your class; as long as your class provided the properties that these methods used, it worked just fine, and there was no "diamond problem" or other multiple-inheritance "gotchas" to worry about.

i would like to see something like this in C# as it would simplify certain kinds of abstraction and contruction issues


Instead of multiple inheritance, you can use mixins which is a better solution.


I think it would over-complicate things without providing enough ROI. We already see people butcher .NET code with too-deep inheritance trees. I can just imagine the atrocities if people had the power to do multiple inheritance.

I won't deny that it has potential, but I just don't see enough benefit.


While there are certainly instances where it can be useful, I have found that most of the time when I think I need it, I really don't.


A colleague wrote this blog about how to get something like multiple inheritance in C# with Dynamic Compilation:

http://www.atalasoft.com/cs/blogs/stevehawley/archive/2008/09/29/late-binding-in-c-using-dynamic-compilation.aspx


I think its simple really. Just like any other complex programming paradigm, you can misuse it and hurt yourself. Can you misuse objects (oh yes!), but that doesn't mean OO is bad in itself.

Similarly with MI. If you do not have a large 'tree' of inherited classes, or a lot of classes that provide the same named methods, then you will be perfectly fine with MI. In fact, as MI provides the implementation, you'll often be better off than a SI implementation where you have to re-code, or cut&paste methods to delegated objects. Less code is better in these cases.. you can make an almighty mess of sharing code by trying to re-use objects through interface inheritance. And such workarounds don't smell right.

I think the single-inheritance model of .NET is flawed: they should have gone with interfaces only, or MI only. Having "half and half" (ie single implementation inheritance plus multiple interface inheritance) is more confusing than it should be, and not elegant at all.

I come from a MI background, and I'm not scared of or burnt by it.


I have posted this here a couple of times but I just think it is really cool. You can learn how to fake MI here. I also think the article highlights why MI is such a pain even if that was not intended.

I neither miss it or need it, I prefer to use composition of objects to achieve my ends. That is really the point of the article as well.


I've used multiple inheritence in C++ myself too, but you really have to know what you're doing in order to not get yourself in trouble, especially if you have two base classes which share a grandparent. Then you can get into issues with virtual inheritence, having to declare every constructor you're going to call down the chain (which makes binary reuse much harder)... it can be a mess.

More importantly, the way the CLI is currently built precludes MI from being implemented easily. I'm sure they could do it if they wanted, but I have other things I'd rather see in the CLI than multiple inheritence.

Things I'd like to see include some features of Spec#, like non-nullable reference types. I'd also like to see more object safety by being able to declare parameters as const, and the ability to declare a function const (meaning that you are guaranteeing that the internal state of an object won't be changed by the method and the compiler double checks you).

I think that between Single Inheritence, Multiple Interface Inheritence, Generics, and Extension Methods, you can do pretty much anything you need to. If anything could improve things for someone desiring MI, I think some sort of language construct which would would allow easier aggregation and composition is needed. That way you can have a shared interface, but then delegate your implementation to a private instance of the class you would normally inherit from. Right now, that takes a lot of boiler plate code to do. Having a more automated language feature for that would help significantly.


I prefer C++. I've used Java, C#, etc. As my programs get more sophisticated in such an OO environment, I find myself missing Multiple Inheritance. That's my subjective experience.

It can make for amazing spaghetti code...it can make for amazingly elegant code.


I believe languages like C# should give the programmer the option. Just because it maybe too complicated does not mean it will be too complicated. Programming languages should provide the developer with tools to build anything the programmer wants to.

You choose to use those API's a developer already wrote, you dont have too.


Give C# implicits and you will not miss multiple inheritance, or any inheritance for that matter.


No I do not. I use all other OO features to develop what I want. I use Interface and object encapsulation and I am never limited on what I want to do.


I try not to use inheritance. The less I can everytime.


다이아몬드 문제가 해결되지 않으면 아니요. 이 문제가 해결되지 않을 때까지 구성을 사용할 수 있습니다.


아니, 우리는 그것에서 벗어났다. 지금 필요합니다.


다중 상속을 도입하면 C ++의 오래된 다이아몬드 문제에 다시 직면하게됩니다.

그러나 피할 수 없다고 생각하는 사람들을 위해 우리는 여전히 컴포지션을 통해 여러 상속 효과를 도입 할 수 있습니다 (개체에 여러 개체를 구성하고 구성된 개체에 책임을 위임하고 반환하는 공용 메서드를 노출) ...

그래서 왜 다중 상속을 가지고 코드를 피할 수없는 예외에 취약하게 만드는지 ...

참조 URL : https://stackoverflow.com/questions/191691/should-c-sharp-have-multiple-inheritance

반응형