[C#] C #의 다중 상속

다중 상속이 나쁘기 때문에 (소스가 더 복잡 해짐) 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의 모든 메소드가 필요하다는 것을 알고 있으므로이 두 가지를 하나의 클래스로 결합하는 것이 가장 쉬운 솔루션입니다.

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



답변

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

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

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

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

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


답변

단지 사용을 고려 구성을 대신 다중 상속을 시뮬레이션하려고합니다. 당신은 성분, 예를 구성하는 어떤 클래스를 정의하는 인터페이스를 사용할 수 있습니다 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();
    }
}


답변

이런 종류의 일을 가능하게 하는 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();


답변

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


답변

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

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


답변

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

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

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

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


답변

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
}