2017-11-14 5 views
-2

이 기본 클래스에서 파생 된 클래스의 유형과 일치하는 멤버를 선언하려는 추상 기본 클래스가 있다고 가정합니다.파생 클래스의 유형 인 추상 기본 클래스의 멤버를 선언하는 방법이 있습니까?

public abstract class BaseClass 
{ 
    protected BaseClass parent; 
} 

public class DerivedClass1 : BaseClass 
{ 
    // parent could be of type DerivedClass2 
} 

public class DerivedClass2 : BaseClass 
{ 
    // parent could be of type DerivedClass1 
} 

각 파생 클래스에서 parent 필드 BaseClass에서 파생 아무것도 할 수 있기 때문에이 작동하지 않습니다. DerivedClass1parent 필드가 DerivedClass1 일 수 있는지 확인하고 싶습니다. 그래서 제네릭을 사용해야한다고 생각합니다.

public abstract class BaseClass<T> where T : BaseClass<T> 
{ 
    protected T parent; 
} 

이것은 혼란스럽고 순환적인 것처럼 보일 수 있지만 컴파일됩니다. 기본적으로 parentBaseClass에서 파생 된 T 유형이어야합니다. 그래서 지금 파생 클래스는 다음과 같이 할 수 있습니다

public class DerivedClass : BaseClass<DerivedClass> 
{ 
    // parent is of type DerivedClass 
} 

문제는 내가 시행해야한다는 유형 일치 내가 DerivedClass를 선언 할 때 자신을.

public class DerivedClass1 : BaseClass<DerivedClass2> 
{ 
    // parent is of type DerivedClass2 
} 

합니까 C#을 기본에 선언 된 멤버의 유형이 파생 유형과 일치해야이 될 수 있도록이 일을하는 방법이있다 :이 같은 일을 누군가를 중지 아무것도?

나는이 ++ 질문을 물어 노력했다이 C와 유사하다 생각 : 내가 제대로 귀하의 요구 사항을 이해한다면 Abstract base class for derived classes with functions that have a return type of the derived class

+2

CRTP라고합니다. – SLaks

+0

상위 속성을 특정 형식으로 만들려면 해당 기본 형식이 상속되는 기본 클래스에 대해 걱정할 필요가있는 이유는 무엇입니까? 또한 추상 클래스에서이 속성을 선언 할 필요가 없습니다. 적절한 클래스에서 속성을 만들어야합니다. –

+0

질문에 불확실한 점이 있습니다. 내 말은, 네가 원하는 것이 분명하다는거야. 하지만, C#이 그런 종류의 제약 조건을 제공하지 않는다는 것을 알았어 야합니다. 더 중요한 점은,이 제약이 유용하다고 생각하는 _why_와 관련된 제로 세부 사항을 제공한다는 것입니다. 'T' 형 매개 변수가 실제로 선언 형과 동일하지 않은 경우 왜 당신에게 중요합니까? 이것이 기본 클래스 또는 파생 클래스의 구현에 어떤 영향을 미칩니 까? 당신이 할 수 없었던 일, 당신이 그런 제약으로 할 수 있었던 것? 기대에 대한 자세한 내용을 입력하십시오. –

답변

1

, 당신은, 상속 관계와 일련의 클래스를 가지고 당신은 그들을 배치 할 트리 구조. 각 인스턴스는 동일한 유형의 부모 만 가지며 동일한 유형의 부모 만 갖습니다. 흥미로운 문제입니다.

조금만이 함께 noodling 후, I는

  1. 상속 관계
  2. 세트 클래스 세트를 가지고 있도록, 두 개의 평행하지만 관련된 객체 그래프에 요구 분리 제안 할 첫 번째 집합의 클래스 중 하나를 포함 할 수있는 클래스는 형식이 엄격한 부모와 자식이 있어야합니다.

먼저, 서로 상속 한 첫 번째 클래스 집합을 선언합시다. 지금은 Node 비트를 무시하십시오.

public class BaseClass 
{ 
    public Node ContainerNode { get; set; } 
} 

public class DerivedClass1 : BaseClass 
{ 
} 

public class DerivedClass2 : BaseClass 
{ 
} 

이러한 클래스는 많은 것을 할 수 없지만 단지 예일뿐입니다.

이제 트리에 참여할 수있는 또 다른 클래스 집합을 설정해 보겠습니다. 트리의 각 요소는 Node입니다.

//Basic node 
public class Node 
{ 
} 

//A node that can contain a T (which must be a BaseClass or derived from one) 
public class Node<T> : Node where T : BaseClass 
{ 
    public T Parent { get; set; } 
    public T This { get; set; } 

    public Node(T innerClass) 
    { 
     this.This = innerClass; 
     innerClass.ContainerNode = this; 
    } 
} 

이제 유형 안전을 위해 필요한 모든 조치를 취했습니다.우리는이처럼 상속 계층 구조의 클래스를 만들 수 있습니다

var child1 = new Node<DerivedClass1>(new DerivedClass1()); 
    var parent1 = new Node<DerivedClass1>(new DerivedClass1()); 
    child1.Parent = parent1.This; 

우리가 실수 DerivedClass1 및 DerivedClass2 섞어 경우의가 어떻게되는지 보자 : 그래서

var child2 = new Node<DerivedClass2>(new DerivedClass2()); 
    var parent2 = new Node<DerivedClass1>(new DerivedClass1()); //Oops 
    child2.Parent = parent2.This; //Does not compile 

을 당신이 볼 수 있듯이, Parent 속성은 이제 형태 보증입니다 .

이제 ^^^^ 모든 것들이 더러워 보입니다. 그래서 몇 가지 도우미 메서드를 추가하여 정리해 보겠습니다.

var child1 = Node.Create(new DerivedClass1()); 
    var parent1 = Node.Create(new DerivedClass1()); 
    child1.Parent = parent1; 

그리고 파생 클래스 중 하나가 자신의 부모 발견하기가 쉽다 :

public class Node 
{ 
    public T GetParent<T>() where T : BaseClass 
    { 
     return ((Node<T>)this).Parent; 
    } 

    static public Node<T> Create<T>(T innerClass) where T : BaseClass 
    { 
     return new Node<T>(innerClass); 
    } 

    static public T GetParent<T>(T child) where T: BaseClass 
    { 
     return child.ContainerNode.GetParent<T>(); 
    } 

    static public implicit operator T (Node<T> input) 
    { 
     return input.This; 
    } 
} 

이제 컴파일러는 <T> 인수를 추론 할 수 있기 때문에, 우리의 선언은 훨씬 깔끔한 있습니다

public class DerivedClass1 : BaseClass 
{ 
    protected DerivedClass1 Parent 
    { 
     get 
     { 
      return Node.GetParent(this); //This is type safe! 
     } 
    } 
} 

이 모든 것에 대한 한 가지 반대 의견은 코드 작성자가이 Node 계층을 처리하지 못하게하려는 것입니다. 우리가 암시 적 캐스트를 설정하기 때문에 글쎄, 그들은하지 않습니다 :

Node<DerivedClass1> a = Node.Create(new DerivedClass1()); 
    DerivedClass1 b = a; //Works!!! And is type-safe. 

Full working code on DotNetFiddle.

+0

매우 인상적입니다! 'ContainerNode','Parent','This'와 같은 멤버는 public이지만 아마도 내부 멤버 일 수 있습니다. –

+1

동의. 또는 당신은 그것들을'public readonly'으로 만들고 생성자 인자를 통해 설정할 수 있습니다. 그들이 불변이라면 그것은 큰 관심사가 아닙니다. –