C #의 다중 상속


212

다중 상속이 나쁘기 때문에 (소스가 더 복잡 해짐) C #은 이러한 패턴을 직접 제공하지 않습니다. 그러나 때때로이 능력을 갖는 것이 도움이 될 것입니다.

예를 들어 인터페이스와 세 가지 클래스를 사용하여 누락 된 다중 상속 패턴을 구현할 수 있습니다.

public interface IFirst { void FirstMethod(); }
public interface ISecond { void SecondMethod(); }

public class First:IFirst 
{ 
    public void FirstMethod() { Console.WriteLine("First"); } 
}

public class Second:ISecond 
{ 
    public void SecondMethod() { Console.WriteLine("Second"); } 
}

public class FirstAndSecond: IFirst, ISecond
{
    First first = new First();
    Second second = new Second();
    public void FirstMethod() { first.FirstMethod(); }
    public void SecondMethod() { second.SecondMethod(); }
}

인터페이스 중 하나에 메소드를 추가 할 때마다 FirstAndSecond 클래스도 변경해야합니다 .

C ++에서 가능한 것처럼 여러 개의 기존 클래스를 하나의 새 클래스에 삽입하는 방법이 있습니까?

어쩌면 어떤 종류의 코드 생성을 사용하는 솔루션이 있습니까?

또는 다음과 같이 보일 수 있습니다 (상상적인 C # 구문).

public class FirstAndSecond: IFirst from First, ISecond from Second
{ }

따라서 인터페이스 중 하나를 수정할 때 FirstAndSecond 클래스를 업데이트 할 필요가 없습니다.


편집하다

실제적인 예를 고려하는 것이 좋습니다.

프로젝트 내부의 다른 위치에서 이미 사용중인 기존 클래스 (예 : ITextTcpClient 기반의 텍스트 기반 TCP 클라이언트)가 있습니다. 이제 Windows 양식 개발자가 쉽게 액세스 할 수 있도록 클래스 구성 요소를 작성해야합니다.

내가 아는 한 현재 두 가지 방법이 있습니다.

  1. 컴포넌트에서 상속 된 새 클래스를 작성하고 FirstAndSecond에 표시된대로 클래스 자체의 인스턴스를 사용하여 TextTcpClient 클래스의 인터페이스를 구현하십시오.

  2. TextTcpClient에서 상속하고 어떻게 든 IComponent를 구현하는 새 클래스를 작성하십시오 (실제로는 아직 시도하지 않았습니다).

두 경우 모두 클래스별로가 아니라 메서드별로 작업해야합니다. TextTcpClient 및 Component의 모든 메소드가 필요하다는 것을 알고 있으므로이 두 가지를 하나의 클래스로 결합하는 것이 가장 쉬운 솔루션입니다.

충돌을 피하기 위해 나중에 코드를 생성하여 결과를 변경할 수 있지만 직접 입력하면 엉덩이에 통증이 생깁니다.


이것이 단순한 다중 상속이 아니라면 얼마나 덜 복잡합니까?
harpo

3.5의 새로운 확장 방법과 작동 방식 (정적 멤버 호출 생성)을 고려할 때 이는 다음 .NET 언어 진화 중 하나 일 수 있습니다.
Larry

때때로 사람들이 왜 그렇게하지 않는지 궁금합니다. 클래스 A : 클래스 B : 클래스 C?
Chibueze Opata

@NazarMerza : 링크가 변경되었습니다. 지금 : 다중 상속 문제 .
Craig McQueen

9
선전이 당신을 속이게하지 마십시오. 당신의 예는 다중 상속이 유용하고 인터페이스가 그것의 부족에 대한 해결책이라는 것을 보여줍니다.
Kemal Erdogan

답변:


125

다중 상속이 나쁘기 때문에 (소스가 더 복잡 해짐) C #은 이러한 패턴을 직접 제공하지 않습니다. 그러나 때때로이 능력을 갖는 것이 도움이 될 것입니다.

C #과 .net CLR은 "소스를 더 복잡하게 만들 것"이 아니라 C #, VB.net과 다른 언어 간의 상호 운용 방식을 아직 결론지지 않았기 때문에 MI를 구현하지 않았습니다.

MI는 유용한 개념이며, 답변되지 않은 질문은 다음과 같은 질문입니다. "다른 수퍼 클래스에 여러 개의 공통 기본 클래스가있는 경우 어떻게합니까?

Perl은 MI가 작동하고 잘 작동하는 유일한 언어입니다. .Net은 언젠가 그것을 소개하지만 아직은 아니지만 CLR은 이미 MI를 지원하지만 내가 말했듯이 아직 그 이상의 언어 구성은 없습니다.

그때까지 프록시 객체와 여러 인터페이스가 붙어 있습니다.


39
CLR은 다중 구현 상속을 지원하지 않으며 다중 인터페이스 상속 만 지원합니다 (C #에서도 지원됨).
Jordão

4
@ Jordão : 완전성을 위해 : 컴파일러가 CLR에서 해당 유형에 대한 MI를 작성할 수 있습니다. 예를 들어 CLS와 호환되지 않습니다. 자세한 내용은 다음 (2004) 기사 blogs.msdn.com/b/csharpfaq/archive/2004/03/07/…를
dvdvorle

2
@ MrHappy : 매우 흥미로운 기사. 실제로 C # 의 Trait Composition 방법을 조사했습니다 .
Jordão

10
@MandeepJanjua 나는 그런 것을 주장하지 않았고, 나는 그것을 잘 소개 할 수 있다고 말했다. ECMA 표준 CLR은 다중 상속을 위해 IL 기계를 제공하지만 아무것도 그것을 완전히 사용하지 않는다는 사실이 남아있다.
IanNorton

4
참고로 다중 상속은 나쁘지 않으며 코드를 복잡하게 만들지 않습니다. 내가 언급하겠다고 생각 했어요
Dmitri Nesteruk

214

단지 사용을 고려 구성을 대신 다중 상속을 시뮬레이션하려고합니다. 당신은 성분, 예를 구성하는 어떤 클래스를 정의하는 인터페이스를 사용할 수 있습니다 ISteerable유형의 속성이 의미를 SteeringWheel, IBrakable유형의 속성 의미 BrakePedal

이 작업을 마치면 C # 3.0에 추가 된 확장 메서드 기능을 사용하여 다음 과 같은 암시 적 속성에 대한 메서드 호출을 더욱 단순화 할 수 있습니다 .

public interface ISteerable { SteeringWheel wheel { get; set; } }

public interface IBrakable { BrakePedal brake { get; set; } }

public class Vehicle : ISteerable, IBrakable
{
    public SteeringWheel wheel { get; set; }

    public BrakePedal brake { get; set; }

    public Vehicle() { wheel = new SteeringWheel(); brake = new BrakePedal(); }
}

public static class SteeringExtensions
{
    public static void SteerLeft(this ISteerable vehicle)
    {
        vehicle.wheel.SteerLeft();
    }
}

public static class BrakeExtensions
{
    public static void Stop(this IBrakable vehicle)
    {
        vehicle.brake.ApplyUntilStop();
    }
}


public class Main
{
    Vehicle myCar = new Vehicle();

    public void main()
    {
        myCar.SteerLeft();
        myCar.Stop();
    }
}

13
그것은 요점입니다-이와 같은 아이디어는 구성을 쉽게 할 것입니다.
Jon Skeet

9
예, 그러나 주요 객체의 일부로 메소드가 실제로 필요한 유스 케이스가 있습니다.
David Pierre

9
불행히도 멤버 변수 데이터는 확장 메서드에서 액세스 할 수 없으므로 내부 또는 (ug) 공개로 노출해야하지만 계약 구성은 다중 상속을 해결하는 가장 좋은 방법이라고 생각합니다.
cfeduke

4
훌륭한 답변! 간결하고 이해하기 쉽고 매우 유용한 일러스트레이션. 감사합니다!
AJ.

3
myCar전화하기 전에 왼쪽으로 스티어링을 마쳤 는지 확인하고 싶을 수도 있습니다 Stop. 속도가 너무 빠를 Stop때 적용 하면 롤오버 될 수 있습니다 myCar. : D
Devraj Gadhavi 2016 년

16

이런 종류의 일을 가능하게 하는 C # 포스트 컴파일러 를 만들었습니다 .

using NRoles;

public interface IFirst { void FirstMethod(); }
public interface ISecond { void SecondMethod(); }

public class RFirst : IFirst, Role {
  public void FirstMethod() { Console.WriteLine("First"); }
}

public class RSecond : ISecond, Role {
  public void SecondMethod() { Console.WriteLine("Second"); }
}

public class FirstAndSecond : Does<RFirst>, Does<RSecond> { }

포스트 컴파일러를 Visual Studio 빌드 후 이벤트로 실행할 수 있습니다.

C : \ some_path \ nroles-v0.1.0-bin \ nutate.exe "$ (TargetPath)"

동일한 어셈블리에서 다음과 같이 사용합니다.

var fas = new FirstAndSecond();
fas.As<RFirst>().FirstMethod();
fas.As<RSecond>().SecondMethod();

다른 어셈블리에서는 다음과 같이 사용합니다.

var fas = new FirstAndSecond();
fas.FirstMethod();
fas.SecondMethod();

6

IFirst와 ISecond를 모두 구현 한 다음 해당 기본 클래스에서 상속하는 추상 기본 클래스를 하나 가질 수 있습니다.


이것은 반드시 좋은 생각 아마도 가장 좋은 방법이지만, : P
leppie

1
interafces에 메소드를 추가 할 때 여전히 추상 클래스를 편집하지 않아도 되겠습니까?
Rik

Rik :이 작업을 한 번만 수행하면 얼마나 게으른가요?
leppie

3
@leppie- "인터페이스 중 하나에 메소드를 추가 할 때마다 FirstAndSecond 클래스를 변경해야합니다." 원래 질문 의이 부분은이 솔루션으로 해결되지 않습니까?
Rik

2
추상 클래스를 편집해야하지만 이에 종속 된 다른 클래스를 편집 할 필요는 없습니다. 벅은 계속해서 전체 클래스 컬렉션에 계단식으로 연결하지 않고 중지합니다.
Joel Coehoorn

3

MI는 나쁘지 않습니다. (심지어) 사용했던 모든 사람들은 그것을 좋아하고 코드를 복잡하게하지 않습니다! 최소한 다른 구조보다 더 이상 코드를 복잡하게 만들 수 없습니다. 잘못된 코드는 MI가 사진에 있는지 여부에 관계없이 잘못된 코드입니다.

어쨌든 공유하고 싶은 다중 상속에 대한 멋진 작은 솔루션을 얻었습니다. http://ra-ajax.org/lsp-liskov-substitution-principle-to-be-or-not-to-be.blog 또는 내 시그의 링크를 따라갈 수 있습니다 ... :)


다중 상속이 가능하고 업 캐스트 및 다운 캐스트를 통해 ID를 보존 할 수 있습니까? 나는 다중 상속의 문제에 대한 알고 솔루션은 정체성 보존되지 않습니다 캐스트 가지고 주위 회전 (경우 myFoo유형 인 Foo에서 상속을 Moo하고 Goo, 모두 상속의에서 Boo다음 (Boo)(Moo)myFoo(Boo)(Goo)myFoo동등하지 않을 것이다). 신원 보호 방법을 알고 있습니까?
supercat

1
web.archive.org 또는 이와 유사한 링크를 사용하여 링크를 따라갈 수는 있지만 여기에서 원래 질문에 제공된 솔루션에 대한 자세한 설명이 나옵니다.
MikeBeaton

2

필자의 구현에서는 MI에 클래스 / 인터페이스를 사용하지만 "양호한 형식"이지만 몇 가지 필요한 함수 호출에 대해서만 다중 상속을 설정해야하기 때문에 복잡한 오버 헤드가 발생하는 경향이 있음을 발견했습니다. 문자 그대로 수십 번 중복되어야했습니다.

대신 OOP 대체의 일종으로 다양한 모듈 종류에서 정적 "기능을 호출하는 기능을 호출하는 기능"을 만드는 것이 더 쉬웠습니다. 내가 작업했던 솔루션은 RPG의 "주문 시스템"으로, 코드를 다시 작성하지 않고도 매우 다양한 철자를 사용하기 위해 함수 호출을 엄청나게 혼합 하여 일치 시켜야 하는 경우가있었습니다.

스펠 로직을위한 인스턴스가 반드시 필요하지 않기 때문에 대부분의 함수는 정적 일 수 있지만, 클래스 상속은 정적 인 동안 가상 또는 추상 키워드를 사용할 수도 없습니다. 인터페이스는 전혀 사용할 수 없습니다.

이 방법으로 코딩이 훨씬 빠르고 깨끗해 보입니다. 함수를 수행하고 상속 된 속성이 필요하지 않은 경우 함수를 사용하십시오.


2

C # 8을 사용하면 인터페이스 멤버의 기본 구현을 통해 실제로 여러 상속이 가능합니다.

interface ILogger
{
    void Log(LogLevel level, string message);
    void Log(Exception ex) => Log(LogLevel.Error, ex.ToString()); // New overload
}

class ConsoleLogger : ILogger
{
    public void Log(LogLevel level, string message) { ... }
    // Log(Exception) gets default implementation
}

4
그렇습니다. 그러나 위의 작업에서는 수행 할 수 없습니다. new ConsoleLogger().Log(someEception)단순히 작동하지 않으므로 ILogger기본 인터페이스 방법을 사용하기 위해 객체를 명시 적으로 캐스팅해야합니다 . 따라서 그 유용성은 다소 제한적입니다.
Dmitri Nesteruk

1

IFirst 및 ISecond의 방법이 IFirst 및 ISecond의 계약과 만 상호 작용해야한다는 제한 사항을 적용 할 수있는 경우 (예와 같이) 확장 방법으로 요청한 작업을 수행 할 수 있습니다. 실제로는 거의 그렇지 않습니다.

public interface IFirst {}
public interface ISecond {}

public class FirstAndSecond : IFirst, ISecond
{
}

public static MultipleInheritenceExtensions
{
  public static void First(this IFirst theFirst)
  {
    Console.WriteLine("First");
  }

  public static void Second(this ISecond theSecond)
  {
    Console.WriteLine("Second");
  }
}

///

public void Test()
{
  FirstAndSecond fas = new FirstAndSecond();
  fas.First();
  fas.Second();
}

따라서 기본 아이디어는 인터페이스에서 필요한 구현을 정의한다는 것입니다.이 필수 항목은 확장 메소드에서 유연한 구현을 지원해야합니다. 확장 인터페이스를 추가하는 대신 "인터페이스에 메서드를 추가"해야합니다.


1

클래스에 메소드를 추가 할 때마다 인터페이스에 서명을 추가해야하므로 인터페이스를 사용하는 것이 번거 롭습니다. 또한 이미 많은 메소드가 있지만 인터페이스가없는 클래스가 있다면 어떨까요? 상속하려는 모든 클래스에 대한 인터페이스를 수동으로 만들어야합니다. 그리고 자식 클래스가 여러 인터페이스에서 상속받는 경우 자식 클래스의 인터페이스에서 모든 메소드를 구현해야합니다.

외관 디자인 패턴을 다음으로 우리가 사용하는 여러 클래스에서 상속 시뮬레이션 할 수 있습니다 접근을 . 상속해야하는 클래스 내에서 {get; set;}을 사용하여 클래스를 속성으로 선언하고 모든 공용 속성과 메서드는 해당 클래스에서 가져온 것이며 자식 클래스의 생성자에서 부모 클래스를 인스턴스화합니다.

예를 들면 다음과 같습니다.

 namespace OOP
 {
     class Program
     {
         static void Main(string[] args)
         {
             Child somechild = new Child();
             somechild.DoHomeWork();
             somechild.CheckingAround();
             Console.ReadLine();
         }
     }

     public class Father 
     {
         public Father() { }
         public void Work()
         {
             Console.WriteLine("working...");
         }
         public void Moonlight()
         {
             Console.WriteLine("moonlighting...");
         }
     }


     public class Mother 
     {
         public Mother() { }
         public void Cook()
         {
             Console.WriteLine("cooking...");
         }
         public void Clean()
         {
             Console.WriteLine("cleaning...");
         }
     }


     public class Child 
     {
         public Father MyFather { get; set; }
         public Mother MyMother { get; set; }

         public Child()
         {
             MyFather = new Father();
             MyMother = new Mother();
         }

         public void GoToSchool()
         {
             Console.WriteLine("go to school...");
         }
         public void DoHomeWork()
         {
             Console.WriteLine("doing homework...");
         }
         public void CheckingAround()
         {
             MyFather.Work();
             MyMother.Cook();
         }
     }


 }

이 구조 클래스를 사용하면 Child 클래스는 클래스 상속 아버지와 어머니의 모든 메소드와 속성에 액세스하여 다중 상속을 시뮬레이션하고 부모 클래스의 인스턴스를 상속합니다. 동일하지는 않지만 실용적입니다.


2
첫 번째 단락에 동의하지 않습니다. 모든 클래스에서 원하는 메소드의 서명 만 인터페이스에 추가합니다. 그러나 원하는만큼 클래스에 추가 메소드를 추가 할 수 있습니다. 또한 마우스 오른쪽 버튼 클릭, 추출 인터페이스가있어 인터페이스 추출 작업이 단순 해집니다. 마지막으로, 귀하의 예는 어떤 식 으로든 상속 (다중 또는 기타)이 아니지만 구성의 훌륭한 예입니다. 아아, 생성자 / 속성 주입을 사용하여 DI / IOC를 시연하기 위해 인터페이스를 사용하면 더 많은 장점이 있습니다. 투표하지 않더라도 좋은 답변은 미안하다고 생각하지 않습니다.
Francis Rodgers

1
1 년 후이 스레드를 되돌아 보면 인터페이스에 서명을 추가하지 않고 클래스에서 원하는만큼 많은 메소드를 추가 할 수 있다는 데 동의하지만 인터페이스가 불완전합니다. 게다가, IDE에서 마우스 오른쪽 버튼을 클릭하여 추출 인터페이스를 찾을 수 없었습니다. 어쩌면 뭔가 빠졌습니다. 그러나 더 큰 관심사는 인터페이스에서 서명을 지정할 때 상속 클래스가 해당 서명을 구현해야한다는 것입니다. 나는 이것이 이중 작업이며 코드 복제로 이어질 수 있다고 생각합니다.
Yogi

EXTRACT 인터페이스 : 클래스 서명을 통해 마우스 오른쪽 버튼으로 클릭 후 인터페이스를 추출 ... VS2015 동일한 프로세스에서 제외하고 는 마우스 오른쪽 버튼을 클릭해야 다음 선택 Quick Actions and Refactorings...이가 알고 있어야합니다, 그것은 당신에게 시간을 많이 절약 할 수
Chef_Code

1

우리 모두는 이것으로 인터페이스 경로를 향하고있는 것처럼 보이지만 여기서 명백한 다른 가능성은 OOP 가해 야 할 일을하고 상속 트리를 구축하는 것입니다 ... (클래스 디자인이 아닌 것은 아닙니다) 약?)

class Program
{
    static void Main(string[] args)
    {
        human me = new human();
        me.legs = 2;
        me.lfType = "Human";
        me.name = "Paul";
        Console.WriteLine(me.name);
    }
}

public abstract class lifeform
{
    public string lfType { get; set; }
}

public abstract class mammal : lifeform 
{
    public int legs { get; set; }
}

public class human : mammal
{
    public string name { get; set; }
}

이 구조는 재사용 가능한 코드 블록을 제공하며 OOP 코드는 어떻게 작성해야합니까?

이 특정 접근 방식이 청구서에 맞지 않으면 필요한 객체를 기반으로 새 클래스를 만듭니다.

class Program
{
    static void Main(string[] args)
    {
        fish shark = new fish();
        shark.size = "large";
        shark.lfType = "Fish";
        shark.name = "Jaws";
        Console.WriteLine(shark.name);
        human me = new human();
        me.legs = 2;
        me.lfType = "Human";
        me.name = "Paul";
        Console.WriteLine(me.name);
    }
}

public abstract class lifeform
{
    public string lfType { get; set; }
}

public abstract class mammal : lifeform 
{
    public int legs { get; set; }
}

public class human : mammal
{
    public string name { get; set; }
}

public class aquatic : lifeform
{
    public string size { get; set; }
}

public class fish : aquatic
{
    public string name { get; set; }
}

0

다중 상속은 일반적으로 해결하는 것보다 더 많은 문제를 일으키는 것들 중 하나입니다. C ++에서는 충분히 매달릴 수있는 로프를 제공하는 패턴에 적합하지만 Java와 C #은 옵션을 제공하지 않는 안전한 경로를 선택했습니다. 가장 큰 문제는 상속자가 구현하지 않은 동일한 서명을 가진 메소드가있는 여러 클래스를 상속하는 경우 수행 할 작업입니다. 어떤 클래스의 방법을 선택해야합니까? 아니면 컴파일하지 않아야합니까? 다중 상속에 의존하지 않는 대부분의 것을 구현하는 다른 방법이 있습니다.


8
C ++로 MI를 판단하지 마십시오. PHP로 OOP를 판단하거나 Pintos로 자동차를 판단하는 것과 같습니다. 이 문제는 쉽게 해결할 수 있습니다. Eiffel에서는 클래스에서 상속 할 때 상속 할 메소드를 지정해야하며 이름을 바꿀 수 있습니다. 모호함도없고 놀라운 것도 없습니다.
Jörg W Mittag

2
@mP : 아니요, Eiffel은 진정한 다중 구현 상속을 제공합니다. 이름을 바꾸는 것이 상속 체인을 잃어버린 것도 아니며 클래스의 캐스팅 가능성을 잃어 버릴 수도 없습니다.
Abel

0

X가 Y를 상속 받으면 다소 직교하는 두 가지 효과가 있습니다.

  1. Y는 X에 기본 기능을 제공하므로 X 코드에는 Y와 다른 항목 만 포함하면됩니다.
  2. Y가 예상되는 거의 모든 곳에서 X가 대신 사용될 수 있습니다.

상속은 두 가지 기능을 모두 제공하지만 둘 중 어느 것도 다른 용도없이 사용할 수있는 상황을 상상하기 어렵지 않습니다. 내가 아는 .net 언어는 직접 사용하지 않는 기본 클래스를 정의하고 아무것도 추가하지 않고 직접 상속되는 하나 이상의 클래스를 사용하여 이러한 기능을 얻을 수 있지만 직접 사용하는 방법은 없습니다. new (이러한 클래스는 모든 코드를 공유 할 수 있지만 서로 대체 할 수는 없습니다). 그러나 모든 CLR 호환 언어를 사용하면 첫 번째 (멤버 재사용)없이 인터페이스의 두 번째 기능 (대체 가능성)을 제공하는 인터페이스를 사용할 수 있습니다.


0

나는 그것이 허용되지 않더라도 알고 있지만 언젠가는 실제로 그 사람들을 위해 그것을 필요로합니다.

class a {}
class b : a {}
class c : b {}

내 경우처럼 나는이 클래스 b : Form (yep windows.forms) class c : b {}

함수의 절반이 동일하고 인터페이스 u를 사용하여 모두 다시 작성해야합니다.


1
귀하의 예는 다중 상속, 그래서 문제가되는 묘사하지 않습니다 당신이 해결하기 위해 노력을? 진정한 다중 상속 예제는 class a : b, c(필요한 계약 구멍을 구현 함) 보여줄 것 입니다. 아마도 당신의 예제는 너무 단순화되어 있습니까?
M.Babcock

0

다중 상속 (MI)에 대한 질문이 수시로 나타나기 때문에 컴포지션 패턴의 일부 문제를 해결하는 접근 방식을 추가하고 싶습니다.

나는에 구축 IFirst, ISecond, First, Second, FirstAndSecond이 문제에서 제시 한 바와 같이, 방법. IFirst인터페이스 / MI 기본 클래스 수에 관계없이 패턴이 동일하게 유지되므로 샘플 코드를로 줄 입니다.

즉, MI로, 가정 수 있습니다 FirstSecond것 같은 기본 클래스에서 모두 파생BaseClass , 만 공용 인터페이스 요소를 사용하여BaseClass

및 구현 BaseClass에서 컨테이너 참조를 추가하여 표현할 수 있습니다 .FirstSecond

class First : IFirst {
  private BaseClass ContainerInstance;
  First(BaseClass container) { ContainerInstance = container; }
  public void FirstMethod() { Console.WriteLine("First"); ContainerInstance.DoStuff(); } 
}
...

것들로부터 보호 인터페이스 요소가 때 더 복잡하게 BaseClass참조하거나 할 때 FirstSecond약간의 추상적 인 부분을 구현하기 위해 자신의 서브 클래스를 요구, MI에서 추상 클래스가 될 것입니다.

class BaseClass {
  protected void DoStuff();
}

abstract class First : IFirst {
  public void FirstMethod() { DoStuff(); DoSubClassStuff(); }
  protected abstract void DoStuff(); // base class reference in MI
  protected abstract void DoSubClassStuff(); // sub class responsibility
}

C #을 사용하면 중첩 클래스가 포함 클래스의 보호 / 개인 요소에 액세스 할 수 있으므로 First구현 에서 추상 비트를 연결하는 데 사용할 수 있습니다 .

class FirstAndSecond : BaseClass, IFirst, ISecond {
  // link interface
  private class PartFirst : First {
    private FirstAndSecond ContainerInstance;
    public PartFirst(FirstAndSecond container) {
      ContainerInstance = container;
    }
    // forwarded references to emulate access as it would be with MI
    protected override void DoStuff() { ContainerInstance.DoStuff(); }
    protected override void DoSubClassStuff() { ContainerInstance.DoSubClassStuff(); }
  }
  private IFirst partFirstInstance; // composition object
  public FirstMethod() { partFirstInstance.FirstMethod(); } // forwarded implementation
  public FirstAndSecond() {
    partFirstInstance = new PartFirst(this); // composition in constructor
  }
  // same stuff for Second
  //...
  // implementation of DoSubClassStuff
  private void DoSubClassStuff() { Console.WriteLine("Private method accessed"); }
}

약간의 상용구가 있지만, FirstMethod와 SecondMethod의 실제 구현이 충분히 복잡하고 액세스 된 개인 / 보호 된 메소드의 양이 적당하면이 패턴이 다중 상속 부족을 극복하는 데 도움이 될 수 있습니다.


0

이것은 Lawrence Wenham의 답변을 따르지만 사용 사례에 따라 개선되었거나 아닐 수도 있습니다. 세터가 필요하지 않습니다.

public interface IPerson {
  int GetAge();
  string GetName();
}

public interface IGetPerson {
  IPerson GetPerson();
}

public static class IGetPersonAdditions {
  public static int GetAgeViaPerson(this IGetPerson getPerson) { // I prefer to have the "ViaPerson" in the name in case the object has another Age property.
    IPerson person = getPerson.GetPersion();
    return person.GetAge();
  }
  public static string GetNameViaPerson(this IGetPerson getPerson) {
    return getPerson.GetPerson().GetName();
  }
}

public class Person: IPerson, IGetPerson {
  private int Age {get;set;}
  private string Name {get;set;}
  public IPerson GetPerson() {
    return this;
  }
  public int GetAge() {  return Age; }
  public string GetName() { return Name; }
}

이제 사람을 얻는 방법을 알고있는 개체는 IGetPerson을 구현할 수 있으며 자동으로 GetAgeViaPerson () 및 GetNameViaPerson () 메서드를 갖습니다. 이 시점에서 기본적으로 모든 Person 코드는 새로운 ivar 이외의 IPerson이 아닌 IGetPerson에 들어가고 둘 다 들어가야합니다. 그리고 이러한 코드를 사용할 때 IGetPerson 개체 자체가 실제로 IPerson인지 여부에 대해 걱정할 필요가 없습니다.


0

이제 여물통 partial클래스 가 가능하며 , 각 클래스는 자체적으로 클래스를 상속하여 최종 객체가 모든 기본 클래스를 상속하게 할 수 있습니다. 자세한 내용은 여기를 참조 하십시오 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.