2012-10-20 2 views
3

같은 기본 클래스에서 모두 파생 된 개체 컬렉션이있는 경우가 있습니다. 컬렉션을 반복하고 각 항목의 유형을 확인하면 해당 객체가 파생 된 유형인지 알 수 있으며 그에 따라 적절하게 처리 할 수 ​​있습니다. 제가 이미 알고있는 것은 파생 된 타입에 대한 체크를 수행하는 쉬운 방법이 있다면 제가 이미하고있는 것입니다. 코드 반복은 일반적으로 필요하지 않으므로, 현재의 방법론은 나에게 조금 떨어져 보인다.기본 클래스 유형의 파생 클래스 유형마다 캐스트를 시도하지 않고도 업 캐스팅 된 객체를 다시 다운 캐스팅 할 수 있습니까?

class A {} 
class B : A {} 
class C : A {} 
class D : C {} 

class Foo 
{ 
    public List<A> Collection { get; set; } 
} 

class Bar 
{ 
    void Iterate() 
    { 
     Foo f = new Foo(); 
     foreach(A item in f.Collection) 
     { 
      DoSomething(a); 
     } 
    } 

    void DoSomething(A a) 
    { 
     ... 

     B b = a as B; 
     if(b != null) 
     { 
      DoSomething(b); 
      return; 
     } 

     C c = a as C; 
     if(c != null) 
     { 
      DoSomething(c); 
      return; 
     } 

     D d = a as D; 
     if(d != null) 
     { 
      DoSomething(d); 
      return; 
     } 
    }; 

    void DoSomething(B a){}; 
    void DoSomething(C a){}; 
    void DoSomething(D a){}; 
} 

나는 모든 웹 서비스가 동일한 결과 유형이 있어야합니다 웹 서비스와 함께 일하고 있습니다.

class WebServiceResult 
{ 
    public bool Success { get; set; } 
    public List<Message> Messages { get; set; } 
} 

class Message 
{ 
    public MessageType Severity { get; set; } // Info, Warning, Error 
    public string Value { get; set; } // 
} 

class InvalidAuthorization: Message 
{ 
    // Severity = MessageType.Error 
    // Value = "Incorrect username." or "Incorrect password", etc. 
} 

class InvalidParameter: Message 
{ 
    // ... 
} 

class ParameterRequired: InvalidParameter 
{ 
    // Severity = MessageType.Error 
    // Value = "Parameter required.", etc. 
    public string ParameterName { get; set; } // 
} 

class CreatePerson: Message 
{ 
    // Severity = MessageType.Info 
    // Value = null 
    public int PersonIdentifier { get; set; } // The id of the newly created person 
} 

목표는 우리가 원하는만큼 많은 종류의 메시지를 클라이언트에 반환 할 수 있다는 것입니다. 웹 서비스 호출 당 하나의 메시지를받는 대신, 피 호출자는 단일 여행에서 모든 실수/성공을 알 수 있고 메시지에서 특정 정보를 파싱하는 문자열을 제거 할 수 있습니다.

원래 제네릭을 사용하는 것이지만 웹 서비스가 다양한 메시지 유형을 가질 수 있으므로 컬렉션이 기본 메시지 클래스를 사용하도록 확장되었습니다.

+0

실제로 달성하려는 것은 무엇입니까? 내가 물어 보는 이유는 당신이 그것에 관해가는 길은 반 패턴과 같은 냄새가 난다는 것입니다. 이런 식으로 많이 던지면 좋지 않은 신호입니다. Liskov Substitution Principle (http://en.wikipedia.org/wiki/Liskov_substitution_principle) –

+1

전체적인 목표를 설명하기 위해 질문을 편집했습니다. – JWilliams

답변

4

ADoSomething를 이동하고 각각의 서브 클래스가 자신의 구현을 제공해야하는 것이 가능할 수있다. 항목을 결정하고, 무엇을해야하는지 알려줍니다. 그것은 그것이 최선의 유형이라는 것을 알기 때문에 무엇을해야 할지를 알고 있습니다. 비록 A에서와 동일하다면 추상 구현이 아닌 가상 구현을 통해 표준 구현을 제공 할 수도 있습니다. 그러나 컴파일러가 구현을 요청하지 않는다는 것을 알고 있어야합니다.

public class A 
{ 
    public virtual DoSomething(){"What A needs doning!"} 
} 

public class B : A 
{ 
    public override DoSomething() {"What B needs doing!"} 
} 

또 다른 방법은 인터페이스를 사용하는 것입니다.

인터페이스 및 추상 기본 클래스가 백그라운드에서 조금 다르게 작동하지만 이는 Lees 제안과 비슷합니다.

일반적으로 상위 클래스를 선호합니다. 왜냐하면 일반적으로 기본 클래스에 표준 동작을 제공하고 다른 경우에만 파생 클래스를 구현하는 경향이 있기 때문입니다.

0
typeof(ParentClass).IsAssignableFrom(typeof(ChildClass)); 

반환 true이면 캐스트가 가능합니다.

또한 수 이러한 방법 :

typeof(ParentClass).IsAssignableFrom(myObject.GetType()); 

하지만 당신의 예제에서

, 당신은 실제로 각 개체 유형에 대한 메소드를 호출. 따라서 리팩토링에 신경 쓰지 않는다면 캐스트를 필요로합니다. 이 같은

뭔가 당신은 과부하를 유지하려는 경우 :

foreach(A item in f.Collection) 
{ 
    Type itemType = item.GetType(); 

    if (typeof(B).IsAssignableFrom(itemType) 
     DoSomethingB(item); 
    else if (typeof(C).IsAssignableFrom(itemType) 
     DoSomethingC(item); 
    //... 
} 

편집 : 좀 더 리의 답변을 좋아합니다. 클래스 형식에 가상/재정의 기능을 추가하면 DoSomething에 실제로 클래스에 속하지 않는 한 더 나은 디자인과 손쉬운 방법이 될 것입니다.

1

기본 클래스 나 인터페이스에 일반 제약 조건을 사용하는 것이 좋습니다.

public class MyClass<T> where T : BaseClass, IInterface 
{ 
    public void executeCode<T>(T param) {}; 
} 

그래서 MyClass<T>executeCode는 방법이 노출 어떤 작업이 전달 된 객체의 데이터에 대해 수행 할 수있는 아이디어가됩니다 만 특정 유형을합니다. 이렇게하면 따라야하는 계약을 지정하기 때문에 전송할 필요가 없습니다.

public abstract class A 
{ 
    abstract void DoSomething(); 
} 

void Iterate() 
{ 
    Foo f = new Foo(); 
    foreach(A item in f.Collection) 
    { 
     item.DoSomething(); 
    } 
} 
관련 문제