2011-03-23 6 views
79

의 상속 된 모든 클래스를 가져 오기? (의사 코드 :나는 추상 클래스가 추상 클래스

CsvExporter 
XmlExporter 

같은 출력

foreach (Implementation imp in Reflection.GetInheritedClasses(AbstractDataExport) 
{ 
    AbstractDataExport derivedClass = Implementation.CallConstructor(); 
    Console.WriteLine(derivedClass.name) 
} 

?

이 아이디어는 AbstractDataExport에서 파생 된 새 클래스를 만들어 모든 구현을 자동으로 반복하고 예를 들어 드롭 다운 목록에 이름을 추가 할 수 있도록하는 것입니다. 난 그냥 프로젝트에서 다른 아무것도 변경하지 않고 파생 클래스를 코딩하고 싶습니다, 다시 컴파일, 빙고!

다른 해결책이있는 경우 알려주십시오.

감사

답변

116

이것은 GUI 애플리케이션에서 특히 많이 볼 수있는 문제이다. BCL 클래스에서이 작업을 수행 할 수 있습니다. 여기 내가 어떻게하는지.

public static class ReflectiveEnumerator 
{ 
    static ReflectiveEnumerator() { } 

    public static IEnumerable<T> GetEnumerableOfType<T>(params object[] constructorArgs) where T : class, IComparable<T> 
    { 
     List<T> objects = new List<T>(); 
     foreach (Type type in 
      Assembly.GetAssembly(typeof(T)).GetTypes() 
      .Where(myType => myType.IsClass && !myType.IsAbstract && myType.IsSubclassOf(typeof(T)))) 
     { 
      objects.Add((T)Activator.CreateInstance(type, constructorArgs)); 
     } 
     objects.Sort(); 
     return objects; 
    } 
} 

몇 가지 참고 사항 :

  • 이 작업의 "비용"에 대해 걱정하지 마십시오 이
      - 당신이 한 번만 일을 할거야가 (희망), 심지어 다음이 느린 아니다 당신이 생각하기에.
    • 기본 클래스가 다른 어셈블리에있을 수 있으므로 Assembly.GetAssembly(typeof(T))을 사용해야합니다.
    • type.IsClass!type.IsAbstract 기준을 사용해야합니다. 인터페이스 또는 추상 클래스를 인스턴스화하려고 시도하면 예외가 발생하기 때문입니다.
    • 열거 된 클래스가 IComparable을 구현하도록 강제로 정렬 할 수 있으므로이를 정렬 할 수 있습니다.
    • 자식 클래스는 생성자 시그니처가 동일해야하며 그렇지 않으면 예외가 발생합니다. 이것은 일반적으로 저에게 문제가되지 않습니다.
  • +1

    유형을 추상적이 아니고 동시에 비 수업으로 지정할 수 있습니까? – user2341923

    +1

    이것은 완전히 미친 짓이야! 나는 당신이 이런 식으로 반성 할 수 있을지 전혀 몰랐다. – CanadaIT

    +0

    @ user2341923 열거 형? – Cesar

    10

    그것은 우아한 방법이 될 수 없습니다하지만 당신은 어셈블리의 모든 클래스를 반복하고 각각에 대해 Type.IsSubclassOf(AbstractDataExport) 를 호출 할 수 있습니다.

    +1

    +1 : 저는 이것이 유일한 해결책이라고 생각합니다. –

    +0

    대단히 감사합니다! 내 솔루션은 귀하의 제안을 기반으로합니다. – trampi

    3

    typeof(AbstractDataExport).Assembly은 유형이있는 어셈블리를 알려줍니다 (모두 동일하다고 가정).

    assembly.GetTypes()은 모든 어셈블리 유형을 제공하며 assembly.GetExportedTypes()은 공개 유형을 제공합니다.

    유형을 반복하고 type.IsAssignableFrom()을 사용하면 유형 유래 여부를 알 수 있습니다.

    +0

    답해 주셔서 감사합니다. assembly.gettypes()는 솔루션에 필요한 것이 었습니다. – trampi

    34

    그들이 모두 같은 어셈블리에 정의되어 있다고 가정하면, 당신은 할 수 있습니다 :

    IEnumerable<AbstractDataExport> exporters = typeof(AbstractDataExport) 
        .Assembly.GetTypes() 
        .Where(t => t.IsSubclassOf(typeof(AbstractDataExport)) && !t.IsAbstract) 
        .Select(t => (AbstractDataExport)Activator.CreateInstance(t)); 
    
    +1

    고맙습니다. 매우 흥미 롭습니다. Activator.CreateInstance'에 대해 좀 더 자세히 살펴볼 필요가있다. – trampi

    +3

    매개 변수없는 생성자가없는 클래스를 인스턴스화하지 못하게하려면't.GetConstructor (Type.EmptyTypes)! = null'을 추가 제약 조건으로 사용하십시오. – mrexodia

    0

    음, 반사 여기에 당신의 친구입니다. 이것을 구현하기 전에, "Reflection은 항상 값 비싸다."-

    +1

    답변 해 주셔서 감사합니다. 실행 속도가 얼마나 느린 지에 대한 정보를 더 주시겠습니까? 비교할만한 게 있나요? :) – trampi