클래스가 일반 클래스에서 파생되었는지 확인


309

파생 클래스가있는 프로젝트에 일반 클래스가 있습니다.

public class GenericClass<T> : GenericInterface<T>
{
}

public class Test : GenericClass<SomeType>
{
}

Type객체가 파생 되었는지 알아내는 방법이 GenericClass있습니까?

t.IsSubclassOf(typeof(GenericClass<>))

작동하지 않습니다.

답변:


430

이 코드를 사용해보십시오

static bool IsSubclassOfRawGeneric(Type generic, Type toCheck) {
    while (toCheck != null && toCheck != typeof(object)) {
        var cur = toCheck.IsGenericType ? toCheck.GetGenericTypeDefinition() : toCheck;
        if (generic == cur) {
            return true;
        }
        toCheck = toCheck.BaseType;
    }
    return false;
}

4
이것은 달콤한 코드 조각입니다. while 루프 구현은 불필요한 재귀 성능 적중도 피합니다. 메타-제네릭 질문에 대한 우아하고 아름다운 솔루션입니다.
EnocNRoll-AnandaGopal Pardue

2
이 프레임 워크의 ReflectionUtils 정적 클래스에이 메서드를 추가했으며 메서드 내에서 toCheck를 Type toCheck = obj.GetType (); "this object obj"가 첫 번째 매개 변수입니다.
EnocNRoll-AnandaGopal Pardue

11
toCheck 유형이 클래스 (예 : 인터페이스)가 아닌 경우 while 루프가 중단되지 않습니다. 이로 인해 NullReferenceException이 발생합니다.
JD Courtoy 2009

2
toCheck가 찾고있는 일반 유형 인 경우 true를 반환합니다.
oillio

14
이것은 구체적인 형식 상속에만 작동합니다 ... 테스트 사례 : bool expect = true; bool actual = Program.IsSubclassOfRawGeneric (typeof (IEnumerable <>), typeof (List <string>)); Assert.AreEqual (예상, 실제); // 실패
Bobby

90

(대규모 재 작성으로 인해 다시 게시 됨)

JaredPar의 코드 답변은 환상적이지만 일반 유형이 값 유형 매개 변수를 기반으로하지 않는 경우 불필요하게 만드는 팁이 있습니다. 나는 "is"연산자가 작동하지 않는 이유에 매달렸다. 그래서 나는 또한 나중에 참조하기 위해 실험 한 결과를 문서화했다. 명확성을 높이려면이 답변을 향상 시키십시오.

팁:

GenericClass 구현이 GenericClassBase와 같은 추상적 인 제네릭이 아닌 기본 클래스에서 상속 받았다고 확신하는 경우 다음과 같이 아무런 문제없이 동일한 질문을 할 수 있습니다.

typeof(Test).IsSubclassOf(typeof(GenericClassBase))

IsSubclassOf ()

내 테스트에 따르면 IsSubclassOf ()는 매개 변수가없는 일반 유형 (예 :

typeof(GenericClass<>)

반면에 작동합니다

typeof(GenericClass<SomeType>)

따라서 SomeType을 기반으로 테스트 할 의사가 있다고 가정하면 GenericClass <>의 파생에 대해 다음 코드가 작동합니다.

typeof(Test).IsSubclassOf(typeof(GenericClass<SomeType>))

GenericClass <>로 테스트하고 싶다고 생각할 수있는 유일한 시간은 플러그인 프레임 워크 시나리오입니다.


"is"연산자에 대한 생각

디자인 타임에 C #에서는 매개 변수가없는 제네릭을 사용할 수 없습니다. 기본적으로 완전한 CLR 형식이 아니기 때문입니다. 따라서 매개 변수를 사용하여 일반 변수를 선언해야하므로 "is"연산자가 객체 작업에 매우 강력합니다. 또한 "is"연산자는 매개 변수가없는 제네릭 형식도 평가할 수 없습니다.

"is"연산자는 인터페이스를 포함한 전체 상속 체인을 테스트합니다.

따라서 객체의 인스턴스가 주어지면 다음 메소드가 트릭을 수행합니다.

bool IsTypeof<T>(object t)
{
    return (t is T);
}

이것은 일종의 중복이지만, 계속해서 모든 사람들에게 시각화 할 것이라고 생각했습니다.

주어진

var t = new Test();

다음 코드 줄은 true를 반환합니다.

bool test1 = IsTypeof<GenericInterface<SomeType>>(t);

bool test2 = IsTypeof<GenericClass<SomeType>>(t);

bool test3 = IsTypeof<Test>(t);

반면에 GenericClass에 특정한 것을 원한다면 좀 더 구체적으로 만들 수 있습니다.

bool IsTypeofGenericClass<SomeType>(object t)
{
    return (t is GenericClass<SomeType>);
}

그런 다음 다음과 같이 테스트하십시오.

bool test1 = IsTypeofGenericClass<SomeType>(t);

2
분석 및 테스트 +1 또한 귀하의 답변은 제 경우에 매우 유용했습니다.
기예르모 구티에레스

3
컴파일러는 .IsSubclassOf (typeof (GenericClass <>))에 완벽하게 만족한다는 점에 유의해야합니다. 원하는대로하지 않습니다.
user2880616

2
그러나 컴파일 할 때 SomeType을 알 수 없으면 어떻게해야합니까?
Ryan The Leach

전체 토론의 요점은 Type물건 이있을 때에 관한 것 입니다.
Jonathan Wood

@JonathanWood 그래서 위의 대답은 typeof연산자를 다루는 이유 입니다. 문서에 따르면 : "typeof 연산자는 형식에 대한 System.Type 개체를 얻는 데 사용됩니다."
EnocNRoll-AnandaGopal Pardue

33

나는이 샘플들 중 일부를 연구했고 어떤 경우에는 부족하다는 것을 알았습니다. 이 버전은 모든 종류의 제네릭, 유형, 인터페이스 및 유형 정의와 함께 작동합니다.

public static bool InheritsOrImplements(this Type child, Type parent)
{
    parent = ResolveGenericTypeDefinition(parent);

    var currentChild = child.IsGenericType
                           ? child.GetGenericTypeDefinition()
                           : child;

    while (currentChild != typeof (object))
    {
        if (parent == currentChild || HasAnyInterfaces(parent, currentChild))
            return true;

        currentChild = currentChild.BaseType != null
                       && currentChild.BaseType.IsGenericType
                           ? currentChild.BaseType.GetGenericTypeDefinition()
                           : currentChild.BaseType;

        if (currentChild == null)
            return false;
    }
    return false;
}

private static bool HasAnyInterfaces(Type parent, Type child)
{
    return child.GetInterfaces()
        .Any(childInterface =>
        {
            var currentInterface = childInterface.IsGenericType
                ? childInterface.GetGenericTypeDefinition()
                : childInterface;

            return currentInterface == parent;
        });
}

private static Type ResolveGenericTypeDefinition(Type parent)
{
    var shouldUseGenericType = true;
    if (parent.IsGenericType && parent.GetGenericTypeDefinition() != parent)
        shouldUseGenericType = false;

    if (parent.IsGenericType && shouldUseGenericType)
        parent = parent.GetGenericTypeDefinition();
    return parent;
}

다음은 단위 테스트입니다.

protected interface IFooInterface
{
}

protected interface IGenericFooInterface<T>
{
}

protected class FooBase
{
}

protected class FooImplementor
    : FooBase, IFooInterface
{
}

protected class GenericFooBase
    : FooImplementor, IGenericFooInterface<object>
{

}

protected class GenericFooImplementor<T>
    : FooImplementor, IGenericFooInterface<T>
{
}


[Test]
public void Should_inherit_or_implement_non_generic_interface()
{
    Assert.That(typeof(FooImplementor)
        .InheritsOrImplements(typeof(IFooInterface)), Is.True);
}

[Test]
public void Should_inherit_or_implement_generic_interface()
{
    Assert.That(typeof(GenericFooBase)
        .InheritsOrImplements(typeof(IGenericFooInterface<>)), Is.True);
}

[Test]
public void Should_inherit_or_implement_generic_interface_by_generic_subclass()
{
    Assert.That(typeof(GenericFooImplementor<>)
        .InheritsOrImplements(typeof(IGenericFooInterface<>)), Is.True);
}

[Test]
public void Should_inherit_or_implement_generic_interface_by_generic_subclass_not_caring_about_generic_type_parameter()
{
    Assert.That(new GenericFooImplementor<string>().GetType()
        .InheritsOrImplements(typeof(IGenericFooInterface<>)), Is.True);
}

[Test]
public void Should_not_inherit_or_implement_generic_interface_by_generic_subclass_not_caring_about_generic_type_parameter()
{
    Assert.That(new GenericFooImplementor<string>().GetType()
        .InheritsOrImplements(typeof(IGenericFooInterface<int>)), Is.False);
}

[Test]
public void Should_inherit_or_implement_non_generic_class()
{
    Assert.That(typeof(FooImplementor)
        .InheritsOrImplements(typeof(FooBase)), Is.True);
}

[Test]
public void Should_inherit_or_implement_any_base_type()
{
    Assert.That(typeof(GenericFooImplementor<>)
        .InheritsOrImplements(typeof(FooBase)), Is.True);
}

2
ResolveGenericTypeDefinition 메서드에 대해 의아해합니다. "shouldUseGenericType"변수에는 실제로 값이 할당됩니다. !parent.IsGenericType || parent.GetGenericTypeDefinition() == parent; 따라서 해당 변수를 if 문의 확장으로 대체하면 다음 if (parent.IsGenericType && shouldUseGenericType)if (parent.IsGenericType && (!parent.IsGenericType || parent.GetGenericTypeDefinition() == parent)) 같이 줄어 듭니다. if (parent.IsGenericType && parent.GetGenericTypeDefinition() == parent)) parent = parent.GetGenericTypeDefinition();
Michael Blackburn

2
아무것도하지 않는 것 같습니다. 이것들이 int j = 0; if (j is an int && j == 0) { j=0; } Am 과 비슷한 값 유형이라면 참조가 같고 값이 혼합되어 있습니까? 메모리에 각 유형의 인스턴스가 하나만 있다고 생각했기 때문에 동일한 유형을 가리키는 두 변수가 실제로 메모리의 동일한 위치를 가리키는 것입니다.
Michael Blackburn

1
@ : MichaelBlackburn on on :) 나는 이것을 다음과 같이 리팩토링했다 : return parent.IsGenericType? parent.GetGenericTypeDefinition () : 부모;
AaronHS

3
이미 부모와 동일한 경우 반환하십시오! 그는 GetGenericTypeDef를 너무 많이 호출하고 있습니다. 한 번만 호출하면됩니다
AaronHS

1
죄송합니다. 아래에 새 의견을 추가하겠습니다.
Menno Deij-반 Rijswijk

26

이 구현은 더 많은 경우 (자식 및 매개 변수 수에 관계없이 시작 매개 변수가 있거나없는 일반 클래스 및 인터페이스)에서 작동하는 것 같습니다.

public static class ReflexionExtension
{
    public static bool IsSubClassOfGeneric(this Type child, Type parent)
    {
        if (child == parent)
            return false;

        if (child.IsSubclassOf(parent))
            return true;

        var parameters = parent.GetGenericArguments();
        var isParameterLessGeneric = !(parameters != null && parameters.Length > 0 &&
            ((parameters[0].Attributes & TypeAttributes.BeforeFieldInit) == TypeAttributes.BeforeFieldInit));

        while (child != null && child != typeof(object))
        {
            var cur = GetFullTypeDefinition(child);
            if (parent == cur || (isParameterLessGeneric && cur.GetInterfaces().Select(i => GetFullTypeDefinition(i)).Contains(GetFullTypeDefinition(parent))))
                return true;
            else if (!isParameterLessGeneric)
                if (GetFullTypeDefinition(parent) == cur && !cur.IsInterface)
                {
                    if (VerifyGenericArguments(GetFullTypeDefinition(parent), cur))
                        if (VerifyGenericArguments(parent, child))
                            return true;
                }
                else
                    foreach (var item in child.GetInterfaces().Where(i => GetFullTypeDefinition(parent) == GetFullTypeDefinition(i)))
                        if (VerifyGenericArguments(parent, item))
                            return true;

            child = child.BaseType;
        }

        return false;
    }

    private static Type GetFullTypeDefinition(Type type)
    {
        return type.IsGenericType ? type.GetGenericTypeDefinition() : type;
    }

    private static bool VerifyGenericArguments(Type parent, Type child)
    {
        Type[] childArguments = child.GetGenericArguments();
        Type[] parentArguments = parent.GetGenericArguments();
        if (childArguments.Length == parentArguments.Length)
            for (int i = 0; i < childArguments.Length; i++)
                if (childArguments[i].Assembly != parentArguments[i].Assembly || childArguments[i].Name != parentArguments[i].Name || childArguments[i].Namespace != parentArguments[i].Namespace)
                    if (!childArguments[i].IsSubclassOf(parentArguments[i]))
                        return false;

        return true;
    }
}

70 76 테스트 사례 는 다음과 같습니다 .

[TestMethod]
public void IsSubClassOfGenericTest()
{
    Assert.IsTrue(typeof(ChildGeneric).IsSubClassOfGeneric(typeof(BaseGeneric<>)), " 1");
    Assert.IsFalse(typeof(ChildGeneric).IsSubClassOfGeneric(typeof(WrongBaseGeneric<>)), " 2");
    Assert.IsTrue(typeof(ChildGeneric).IsSubClassOfGeneric(typeof(IBaseGeneric<>)), " 3");
    Assert.IsFalse(typeof(ChildGeneric).IsSubClassOfGeneric(typeof(IWrongBaseGeneric<>)), " 4");
    Assert.IsTrue(typeof(IChildGeneric).IsSubClassOfGeneric(typeof(IBaseGeneric<>)), " 5");
    Assert.IsFalse(typeof(IWrongBaseGeneric<>).IsSubClassOfGeneric(typeof(ChildGeneric2<>)), " 6");
    Assert.IsTrue(typeof(ChildGeneric2<>).IsSubClassOfGeneric(typeof(BaseGeneric<>)), " 7");
    Assert.IsTrue(typeof(ChildGeneric2<Class1>).IsSubClassOfGeneric(typeof(BaseGeneric<>)), " 8");
    Assert.IsTrue(typeof(ChildGeneric).IsSubClassOfGeneric(typeof(BaseGeneric<Class1>)), " 9");
    Assert.IsFalse(typeof(ChildGeneric).IsSubClassOfGeneric(typeof(WrongBaseGeneric<Class1>)), "10");
    Assert.IsTrue(typeof(ChildGeneric).IsSubClassOfGeneric(typeof(IBaseGeneric<Class1>)), "11");
    Assert.IsFalse(typeof(ChildGeneric).IsSubClassOfGeneric(typeof(IWrongBaseGeneric<Class1>)), "12");
    Assert.IsTrue(typeof(IChildGeneric).IsSubClassOfGeneric(typeof(IBaseGeneric<Class1>)), "13");
    Assert.IsFalse(typeof(BaseGeneric<Class1>).IsSubClassOfGeneric(typeof(ChildGeneric2<Class1>)), "14");
    Assert.IsTrue(typeof(ChildGeneric2<Class1>).IsSubClassOfGeneric(typeof(BaseGeneric<Class1>)), "15");
    Assert.IsFalse(typeof(ChildGeneric).IsSubClassOfGeneric(typeof(ChildGeneric)), "16");
    Assert.IsFalse(typeof(IChildGeneric).IsSubClassOfGeneric(typeof(IChildGeneric)), "17");
    Assert.IsFalse(typeof(IBaseGeneric<>).IsSubClassOfGeneric(typeof(IChildGeneric2<>)), "18");
    Assert.IsTrue(typeof(IChildGeneric2<>).IsSubClassOfGeneric(typeof(IBaseGeneric<>)), "19");
    Assert.IsTrue(typeof(IChildGeneric2<Class1>).IsSubClassOfGeneric(typeof(IBaseGeneric<>)), "20");
    Assert.IsFalse(typeof(IBaseGeneric<Class1>).IsSubClassOfGeneric(typeof(IChildGeneric2<Class1>)), "21");
    Assert.IsTrue(typeof(IChildGeneric2<Class1>).IsSubClassOfGeneric(typeof(IBaseGeneric<Class1>)), "22");
    Assert.IsFalse(typeof(IBaseGeneric<Class1>).IsSubClassOfGeneric(typeof(BaseGeneric<Class1>)), "23");
    Assert.IsTrue(typeof(BaseGeneric<Class1>).IsSubClassOfGeneric(typeof(IBaseGeneric<Class1>)), "24");
    Assert.IsFalse(typeof(IBaseGeneric<>).IsSubClassOfGeneric(typeof(BaseGeneric<>)), "25");
    Assert.IsTrue(typeof(BaseGeneric<>).IsSubClassOfGeneric(typeof(IBaseGeneric<>)), "26");
    Assert.IsTrue(typeof(BaseGeneric<Class1>).IsSubClassOfGeneric(typeof(IBaseGeneric<>)), "27");
    Assert.IsFalse(typeof(IBaseGeneric<Class1>).IsSubClassOfGeneric(typeof(IBaseGeneric<Class1>)), "28");
    Assert.IsTrue(typeof(BaseGeneric2<Class1>).IsSubClassOfGeneric(typeof(IBaseGeneric<Class1>)), "29");
    Assert.IsFalse(typeof(IBaseGeneric<>).IsSubClassOfGeneric(typeof(BaseGeneric2<>)), "30");
    Assert.IsTrue(typeof(BaseGeneric2<>).IsSubClassOfGeneric(typeof(IBaseGeneric<>)), "31");
    Assert.IsTrue(typeof(BaseGeneric2<Class1>).IsSubClassOfGeneric(typeof(IBaseGeneric<>)), "32");
    Assert.IsTrue(typeof(ChildGenericA).IsSubClassOfGeneric(typeof(BaseGenericA<,>)), "33");
    Assert.IsFalse(typeof(ChildGenericA).IsSubClassOfGeneric(typeof(WrongBaseGenericA<,>)), "34");
    Assert.IsTrue(typeof(ChildGenericA).IsSubClassOfGeneric(typeof(IBaseGenericA<,>)), "35");
    Assert.IsFalse(typeof(ChildGenericA).IsSubClassOfGeneric(typeof(IWrongBaseGenericA<,>)), "36");
    Assert.IsTrue(typeof(IChildGenericA).IsSubClassOfGeneric(typeof(IBaseGenericA<,>)), "37");
    Assert.IsFalse(typeof(IWrongBaseGenericA<,>).IsSubClassOfGeneric(typeof(ChildGenericA2<,>)), "38");
    Assert.IsTrue(typeof(ChildGenericA2<,>).IsSubClassOfGeneric(typeof(BaseGenericA<,>)), "39");
    Assert.IsTrue(typeof(ChildGenericA2<ClassA, ClassB>).IsSubClassOfGeneric(typeof(BaseGenericA<,>)), "40");
    Assert.IsTrue(typeof(ChildGenericA).IsSubClassOfGeneric(typeof(BaseGenericA<ClassA, ClassB>)), "41");
    Assert.IsFalse(typeof(ChildGenericA).IsSubClassOfGeneric(typeof(WrongBaseGenericA<ClassA, ClassB>)), "42");
    Assert.IsTrue(typeof(ChildGenericA).IsSubClassOfGeneric(typeof(IBaseGenericA<ClassA, ClassB>)), "43");
    Assert.IsFalse(typeof(ChildGenericA).IsSubClassOfGeneric(typeof(IWrongBaseGenericA<ClassA, ClassB>)), "44");
    Assert.IsTrue(typeof(IChildGenericA).IsSubClassOfGeneric(typeof(IBaseGenericA<ClassA, ClassB>)), "45");
    Assert.IsFalse(typeof(BaseGenericA<ClassA, ClassB>).IsSubClassOfGeneric(typeof(ChildGenericA2<ClassA, ClassB>)), "46");
    Assert.IsTrue(typeof(ChildGenericA2<ClassA, ClassB>).IsSubClassOfGeneric(typeof(BaseGenericA<ClassA, ClassB>)), "47");
    Assert.IsFalse(typeof(ChildGenericA).IsSubClassOfGeneric(typeof(ChildGenericA)), "48");
    Assert.IsFalse(typeof(IChildGenericA).IsSubClassOfGeneric(typeof(IChildGenericA)), "49");
    Assert.IsFalse(typeof(IBaseGenericA<,>).IsSubClassOfGeneric(typeof(IChildGenericA2<,>)), "50");
    Assert.IsTrue(typeof(IChildGenericA2<,>).IsSubClassOfGeneric(typeof(IBaseGenericA<,>)), "51");
    Assert.IsTrue(typeof(IChildGenericA2<ClassA, ClassB>).IsSubClassOfGeneric(typeof(IBaseGenericA<,>)), "52");
    Assert.IsFalse(typeof(IBaseGenericA<ClassA, ClassB>).IsSubClassOfGeneric(typeof(IChildGenericA2<ClassA, ClassB>)), "53");
    Assert.IsTrue(typeof(IChildGenericA2<ClassA, ClassB>).IsSubClassOfGeneric(typeof(IBaseGenericA<ClassA, ClassB>)), "54");
    Assert.IsFalse(typeof(IBaseGenericA<ClassA, ClassB>).IsSubClassOfGeneric(typeof(BaseGenericA<ClassA, ClassB>)), "55");
    Assert.IsTrue(typeof(BaseGenericA<ClassA, ClassB>).IsSubClassOfGeneric(typeof(IBaseGenericA<ClassA, ClassB>)), "56");
    Assert.IsFalse(typeof(IBaseGenericA<,>).IsSubClassOfGeneric(typeof(BaseGenericA<,>)), "57");
    Assert.IsTrue(typeof(BaseGenericA<,>).IsSubClassOfGeneric(typeof(IBaseGenericA<,>)), "58");
    Assert.IsTrue(typeof(BaseGenericA<ClassA, ClassB>).IsSubClassOfGeneric(typeof(IBaseGenericA<,>)), "59");
    Assert.IsFalse(typeof(IBaseGenericA<ClassA, ClassB>).IsSubClassOfGeneric(typeof(IBaseGenericA<ClassA, ClassB>)), "60");
    Assert.IsTrue(typeof(BaseGenericA2<ClassA, ClassB>).IsSubClassOfGeneric(typeof(IBaseGenericA<ClassA, ClassB>)), "61");
    Assert.IsFalse(typeof(IBaseGenericA<,>).IsSubClassOfGeneric(typeof(BaseGenericA2<,>)), "62");
    Assert.IsTrue(typeof(BaseGenericA2<,>).IsSubClassOfGeneric(typeof(IBaseGenericA<,>)), "63");
    Assert.IsTrue(typeof(BaseGenericA2<ClassA, ClassB>).IsSubClassOfGeneric(typeof(IBaseGenericA<,>)), "64");
    Assert.IsFalse(typeof(BaseGenericA2<ClassB, ClassA>).IsSubClassOfGeneric(typeof(IBaseGenericA<ClassA, ClassB>)), "65");
    Assert.IsFalse(typeof(BaseGenericA<ClassB, ClassA>).IsSubClassOfGeneric(typeof(ChildGenericA2<ClassA, ClassB>)), "66");
    Assert.IsFalse(typeof(BaseGenericA2<ClassB, ClassA>).IsSubClassOfGeneric(typeof(BaseGenericA<ClassA, ClassB>)), "67");
    Assert.IsTrue(typeof(ChildGenericA3<ClassA, ClassB>).IsSubClassOfGeneric(typeof(BaseGenericB<ClassA, ClassB, ClassC>)), "68");
    Assert.IsTrue(typeof(ChildGenericA4<ClassA, ClassB>).IsSubClassOfGeneric(typeof(IBaseGenericB<ClassA, ClassB, ClassC>)), "69");
    Assert.IsFalse(typeof(ChildGenericA3<ClassB, ClassA>).IsSubClassOfGeneric(typeof(BaseGenericB<ClassA, ClassB, ClassC>)), "68-2");
    Assert.IsTrue(typeof(ChildGenericA3<ClassA, ClassB2>).IsSubClassOfGeneric(typeof(BaseGenericB<ClassA, ClassB, ClassC>)), "68-3");
    Assert.IsFalse(typeof(ChildGenericA3<ClassB2, ClassA>).IsSubClassOfGeneric(typeof(BaseGenericB<ClassA, ClassB, ClassC>)), "68-4");
    Assert.IsFalse(typeof(ChildGenericA4<ClassB, ClassA>).IsSubClassOfGeneric(typeof(IBaseGenericB<ClassA, ClassB, ClassC>)), "69-2");
    Assert.IsTrue(typeof(ChildGenericA4<ClassA, ClassB2>).IsSubClassOfGeneric(typeof(IBaseGenericB<ClassA, ClassB, ClassC>)), "69-3");
    Assert.IsFalse(typeof(ChildGenericA4<ClassB2, ClassA>).IsSubClassOfGeneric(typeof(IBaseGenericB<ClassA, ClassB, ClassC>)), "69-4");
    Assert.IsFalse(typeof(bool).IsSubClassOfGeneric(typeof(IBaseGenericB<ClassA, ClassB, ClassC>)), "70");
}

테스트를위한 클래스와 인터페이스

public class Class1 { }
public class BaseGeneric<T> : IBaseGeneric<T> { }
public class BaseGeneric2<T> : IBaseGeneric<T>, IInterfaceBidon { }
public interface IBaseGeneric<T> { }
public class ChildGeneric : BaseGeneric<Class1> { }
public interface IChildGeneric : IBaseGeneric<Class1> { }
public class ChildGeneric2<Class1> : BaseGeneric<Class1> { }
public interface IChildGeneric2<Class1> : IBaseGeneric<Class1> { }

public class WrongBaseGeneric<T> { }
public interface IWrongBaseGeneric<T> { }

public interface IInterfaceBidon { }

public class ClassA { }
public class ClassB { }
public class ClassC { }
public class ClassB2 : ClassB { }
public class BaseGenericA<T, U> : IBaseGenericA<T, U> { }
public class BaseGenericB<T, U, V> { }
public interface IBaseGenericB<ClassA, ClassB, ClassC> { }
public class BaseGenericA2<T, U> : IBaseGenericA<T, U>, IInterfaceBidonA { }
public interface IBaseGenericA<T, U> { }
public class ChildGenericA : BaseGenericA<ClassA, ClassB> { }
public interface IChildGenericA : IBaseGenericA<ClassA, ClassB> { }
public class ChildGenericA2<ClassA, ClassB> : BaseGenericA<ClassA, ClassB> { }
public class ChildGenericA3<ClassA, ClassB> : BaseGenericB<ClassA, ClassB, ClassC> { }
public class ChildGenericA4<ClassA, ClassB> : IBaseGenericB<ClassA, ClassB, ClassC> { }
public interface IChildGenericA2<ClassA, ClassB> : IBaseGenericA<ClassA, ClassB> { }

public class WrongBaseGenericA<T, U> { }
public interface IWrongBaseGenericA<T, U> { }

public interface IInterfaceBidonA { }

4
이것은 나를 위해 일한 유일한 솔루션입니다. 여러 유형 매개 변수가있는 클래스와 함께 작동하는 다른 솔루션을 찾을 수 없습니다. 감사합니다.
코너 클라크

1
이 모든 테스트 사례를 게시 해 주셔서 감사합니다. 왼쪽에 ClassB, ClassA가 있고 오른쪽에 ClassA, ClassB가 있기 때문에 사례 68과 69는 사실 대신 거짓이어야한다고 생각합니다.
Grax32

맞습니다, @Grax. 지금 수정할 시간이 없지만 게시물이 완료 되 자마자 업데이트하겠습니다. 수정은 "VerifyGenericArguments"방식이어야한다고 생각합니다.
Xav987

1
@Grax : 정정 할 시간이있었습니다. ClassB2 클래스를 추가하고 VerifyGenericArguments를 변경했으며 VerifyGenericArguments를 호출 할 때 컨트롤을 추가했습니다. 또한 사례 68과 69를 수정하고 68-2, 68-3, 68-4, 69-2, 69-3 및 69-4를 추가했습니다.
Xav987

1
감사합니다. 작업 솔루션 및 엄청난 양의 테스트 사례에 대해 +1 (적어도 나에게는 엄청나 다).
Eldoïr

10

JaredPar의 코드는 하나의 상속 수준에 대해서만 작동합니다. 상속의 무제한 레벨을 위해서는 다음 코드를 사용하십시오.

public bool IsTypeDerivedFromGenericType(Type typeToCheck, Type genericType)
{
    if (typeToCheck == typeof(object))
    {
        return false;
    }
    else if (typeToCheck == null)
    {
        return false;
    }
    else if (typeToCheck.IsGenericType && typeToCheck.GetGenericTypeDefinition() == genericType)
    {
        return true;
    }
    else
    {
        return IsTypeDerivedFromGenericType(typeToCheck.BaseType, genericType);
    }
}

4
whileJaredPar의 코드에서 무제한 수준을 다룹니다.
Jay

@jay ... 그리고 재귀를 피하십시오.
Marc L.

1
@MarcL. 이것은 꼬리 재귀를 사용하므로 컴파일러가 재귀를 최적화하는 것은 쉽지 않습니다.
Darhuuk

10

다음은 객체가 특정 유형에서 파생되었는지 확인하기 위해 만든 작은 방법입니다. 나를 위해 잘 작동합니다!

internal static bool IsDerivativeOf(this Type t, Type typeToCompare)
{
    if (t == null) throw new NullReferenceException();
    if (t.BaseType == null) return false;

    if (t.BaseType == typeToCompare) return true;
    else return t.BaseType.IsDerivativeOf(typeToCompare);
}

7

과잉 일 수 있지만 다음과 같은 확장 방법을 사용합니다. 서브 클래스뿐만 아니라 인터페이스도 점검합니다. 지정된 일반 정의가있는 유형을 리턴 할 수도 있습니다.

예를 들어 문제의 예에서는 일반 클래스뿐만 아니라 일반 인터페이스에 대해서도 테스트 할 수 있습니다. 리턴 된 유형을 GetGenericArguments일반 인수 유형이 "SomeType"인지 판별하는 데 사용할 수 있습니다 .

/// <summary>
/// Checks whether this type has the specified definition in its ancestry.
/// </summary>   
public static bool HasGenericDefinition(this Type type, Type definition)
{
    return GetTypeWithGenericDefinition(type, definition) != null;
}

/// <summary>
/// Returns the actual type implementing the specified definition from the
/// ancestry of the type, if available. Else, null.
/// </summary>
public static Type GetTypeWithGenericDefinition(this Type type, Type definition)
{
    if (type == null)
        throw new ArgumentNullException("type");
    if (definition == null)
        throw new ArgumentNullException("definition");
    if (!definition.IsGenericTypeDefinition)
        throw new ArgumentException(
            "The definition needs to be a GenericTypeDefinition", "definition");

    if (definition.IsInterface)
        foreach (var interfaceType in type.GetInterfaces())
            if (interfaceType.IsGenericType
                && interfaceType.GetGenericTypeDefinition() == definition)
                return interfaceType;

    for (Type t = type; t != null; t = t.BaseType)
        if (t.IsGenericType && t.GetGenericTypeDefinition() == definition)
            return t;

    return null;
}

좋은 소식! 두 가지 방법으로 우려를 나누는 것이 좋습니다.
Wiebe Tijsma

4

fir3rpho3nixx와 David Schmitt의 위의 훌륭한 답변을 바탕으로 코드를 수정하고 ShouldInheritOrImplementTypedGenericInterface 테스트 (마지막)를 추가했습니다.

    /// <summary>
    /// Find out if a child type implements or inherits from the parent type.
    /// The parent type can be an interface or a concrete class, generic or non-generic.
    /// </summary>
    /// <param name="child"></param>
    /// <param name="parent"></param>
    /// <returns></returns>
    public static bool InheritsOrImplements(this Type child, Type parent)
    {
        var currentChild = parent.IsGenericTypeDefinition && child.IsGenericType ? child.GetGenericTypeDefinition() : child;

        while (currentChild != typeof(object))
        {
            if (parent == currentChild || HasAnyInterfaces(parent, currentChild))
                return true;

            currentChild = currentChild.BaseType != null && parent.IsGenericTypeDefinition && currentChild.BaseType.IsGenericType
                                ? currentChild.BaseType.GetGenericTypeDefinition()
                                : currentChild.BaseType;

            if (currentChild == null)
                return false;
        }
        return false;
    }

    private static bool HasAnyInterfaces(Type parent, Type child)
    {
        return child.GetInterfaces().Any(childInterface =>
            {
                var currentInterface = parent.IsGenericTypeDefinition && childInterface.IsGenericType
                    ? childInterface.GetGenericTypeDefinition()
                    : childInterface;

                return currentInterface == parent;
            });

    }

    [Test]
    public void ShouldInheritOrImplementNonGenericInterface()
    {
        Assert.That(typeof(FooImplementor)
            .InheritsOrImplements(typeof(IFooInterface)), Is.True);
    }

    [Test]
    public void ShouldInheritOrImplementGenericInterface()
    {
        Assert.That(typeof(GenericFooBase)
            .InheritsOrImplements(typeof(IGenericFooInterface<>)), Is.True);
    }

    [Test]
    public void ShouldInheritOrImplementGenericInterfaceByGenericSubclass()
    {
        Assert.That(typeof(GenericFooImplementor<>)
            .InheritsOrImplements(typeof(IGenericFooInterface<>)), Is.True);
    }

    [Test]
    public void ShouldInheritOrImplementGenericInterfaceByGenericSubclassNotCaringAboutGenericTypeParameter()
    {
        Assert.That(new GenericFooImplementor<string>().GetType()
            .InheritsOrImplements(typeof(IGenericFooInterface<>)), Is.True);
    }

    [Test]
    public void ShouldNotInheritOrImplementGenericInterfaceByGenericSubclassNotCaringAboutGenericTypeParameter()
    {
        Assert.That(new GenericFooImplementor<string>().GetType()
            .InheritsOrImplements(typeof(IGenericFooInterface<int>)), Is.False);
    }

    [Test]
    public void ShouldInheritOrImplementNonGenericClass()
    {
        Assert.That(typeof(FooImplementor)
            .InheritsOrImplements(typeof(FooBase)), Is.True);
    }

    [Test]
    public void ShouldInheritOrImplementAnyBaseType()
    {
        Assert.That(typeof(GenericFooImplementor<>)
            .InheritsOrImplements(typeof(FooBase)), Is.True);
    }

    [Test]
    public void ShouldInheritOrImplementTypedGenericInterface()
    {
        GenericFooImplementor<int> obj = new GenericFooImplementor<int>();
        Type t = obj.GetType();

        Assert.IsTrue(t.InheritsOrImplements(typeof(IGenericFooInterface<int>)));
        Assert.IsFalse(t.InheritsOrImplements(typeof(IGenericFooInterface<String>)));
    } 

4

이것은 linq를 사용하여 쉽게 수행 할 수 있습니다. 일반 기본 클래스 GenericBaseType의 서브 클래스 인 모든 유형을 찾습니다.

    IEnumerable<Type> allTypes = Assembly.GetExecutingAssembly().GetTypes();

    IEnumerable<Type> mySubclasses = allTypes.Where(t => t.BaseType != null 
                                                            && t.BaseType.IsGenericType
                                                            && t.BaseType.GetGenericTypeDefinition() == typeof(GenericBaseType<,>));

이것은 나를 위해 일한 유일한 솔루션이었습니다. 간단하고 우아합니다. 감사합니다.
silkfire

4

간단한 해결책 : 제네릭이 아닌 일반 인터페이스를 만들고 일반 클래스에 추가하십시오.

public interface IGenericClass
{
}

public class GenericClass<T> : GenericInterface<T>, IGenericClass
{
}

그럼 그냥 어떤 방법으로 사용 좋아 것을 확인 is, as, IsAssignableFrom, 등

if (thing is IGenericClass)
{
    // Do work
{

OP가 가지고있는 일반 클래스를 편집 할 수있는 경우에만 가능하지만 암호 확장 방법을 사용하는 것보다 약간 우아하고 읽기 쉽습니다.


1
그러나, 뭔가 유형인지 여부를 확인 IGenericClass당신을 보장하지 않습니다 GenericClass또는 GenericInterface실제로 확장 또는 구현됩니다. 즉, 컴파일러에서도 일반 클래스의 멤버에 액세스 할 수 없습니다.
B12Toaster

4

@jaredpar의 답변에 추가 된 인터페이스를 확인하는 데 사용하는 내용은 다음과 같습니다.

public static bool IsImplementerOfRawGeneric(this Type type, Type toCheck)
{
    if (toCheck.GetTypeInfo().IsClass)
    {
        return false;
    }

    return type.GetInterfaces().Any(interfaceType =>
    {
        var current = interfaceType.GetTypeInfo().IsGenericType ?
                    interfaceType.GetGenericTypeDefinition() : interfaceType;
        return current == toCheck;
    });
}

public static bool IsSubTypeOfRawGeneric(this Type type, Type toCheck)
{
    return type.IsInterface ?
          IsImplementerOfRawGeneric(type, toCheck)
        : IsSubclassOfRawGeneric(type, toCheck);
}

전의:

Console.WriteLine(typeof(IList<>).IsSubTypeOfRawGeneric(typeof(IList<int>))); // true

좋은 추가. 당신과 jaredpar에게 공감대를주었습니다. 내 유일한 의견은 확장 방법으로 인해 jaredpar의 답변에서 유형의 위치를 ​​반대로 한 것입니다. 나는 그것을 확장 방법으로 제거하고 조금 버렸다. 문제가 아니라 내 것. 다음 사람에게 머리를 돌리고 싶었습니다. 다시 감사합니다.
Tony

@Tony, 팁 주셔서 감사하지만 다음에 답변을 업데이트하십시오.
johnny 5

@ vexe, 테스트는 원래 답변이 깨지는 것이 중요하며 인터페이스에서 테스트했기 때문에 작동합니다. 둘째, 유형에 관계없이이 기능을 실행하여 처리 능력을 낭비하고 있습니다.
johnny 5

2

자레드 파,

typeof (type <>)을 toCheck로 전달하면 작동하지 않습니다. 여기 내가 바꿨 던 것이 있습니다.

static bool IsSubclassOfRawGeneric(Type generic, Type toCheck) {
    while (toCheck != typeof(object)) {
        var cur = toCheck.IsGenericType ? toCheck.GetGenericTypeDefinition() : toCheck;
          if (cur.IsGenericType && generic.GetGenericTypeDefinition() == cur.GetGenericTypeDefinition()) {
            return true;
        }
        toCheck = toCheck.BaseType;
    }
    return false;
}

JaredPar의 솔루션은 실제로 작동 typeof(type<>)으로 toCheck. 또한 JaredPar의 솔루션에서와 같이 null 검사가 실제로 필요합니다. 또한, 나는 일반적인 유형에서만 작동하도록 메소드를 제한하는 것 이외의 다른 cur == generic솔루션으로 대체하여 달성하고있는 것이 무엇인지 모른다 cur.IsGenericType && generic.GetGenericTypeDefinition() == cur.GetGenericTypeDefinition(). 다시 말해, 이와 같은 것은 예외로 실패합니다 :IsSubclassOfRawGeneric(typeof(MyClass), typeof(MyClass<>))
nawfal

2

@EnocNRoll-Ananda Gopal의 답변은 흥미롭지 만 인스턴스가 미리 인스턴스화되지 않았거나 일반 유형 정의로 확인하려는 경우이 방법을 제안합니다.

public static bool TypeIs(this Type x, Type d) {
    if(null==d) {
        return false;
    }

    for(var c = x; null!=c; c=c.BaseType) {
        var a = c.GetInterfaces();

        for(var i = a.Length; i-->=0;) {
            var t = i<0 ? c : a[i];

            if(t==d||t.IsGenericType&&t.GetGenericTypeDefinition()==d) {
                return true;
            }
        }
    }

    return false;
}

다음과 같이 사용하십시오.

var b = typeof(char[]).TypeIs(typeof(IList<>)); // true

이 두 경우 네 가지 조건의 경우는 t(테스트 할)과 d일반적인 종류 두 가지 경우가 덮여에 의해 t==d하는 수 있습니다 (1)도 td일반적인 정의이다 또는 (2) 둘 다 일반적인 정의입니다 . 나머지 사례는 그중 하나가 일반 정의 d이며 이미 일반 정의 일 때만 a td 그렇지 않다고 .

테스트하려는 임의의 클래스 또는 인터페이스에서 작동해야하며 is연산자를 사용하여 해당 유형의 인스턴스를 테스트하는 것처럼 리턴합니다 .


0
Type _type = myclass.GetType();
PropertyInfo[] _propertyInfos = _type.GetProperties();
Boolean _test = _propertyInfos[0].PropertyType.GetGenericTypeDefinition() 
== typeof(List<>);

0

이 확장을 시도해 볼 수 있습니다

    public static bool IsSubClassOfGenericClass(this Type type, Type genericClass,Type t)
    {
        return type.IsSubclassOf(genericClass.MakeGenericType(new[] { t }));
    }

0

이 게임에 늦게 ... 나도 JarodPar의 대답에 대한 또 다른 순열이 있습니다.

리플렉터에 대한 Type.IsSubClassOf (Type) 제공 :

    public virtual bool IsSubclassOf(Type c)
    {
        Type baseType = this;
        if (!(baseType == c))
        {
            while (baseType != null)
            {
                if (baseType == c)
                {
                    return true;
                }
                baseType = baseType.BaseType;
            }
            return false;
        }
        return false;
    }

그것으로부터, 우리는 그것이 크레이 크레이를하고있는 것을하지 않고 JaredPar의 반복적 접근과 유사하다는 것을 알 수 있습니다. 여태까지는 그런대로 잘됐다. 여기 내 버전이 있습니다 (면책 조항 : 철저히 테스트되지 않았으므로 lemme에서 문제를 발견하면 알 수 있습니다)

    public static bool IsExtension(this Type thisType, Type potentialSuperType)
    {
        //
        // protect ya neck
        //
        if (thisType == null || potentialSuperType == null || thisType == potentialSuperType) return false;

        //
        // don't need to traverse inheritance for interface extension, so check/do these first
        //
        if (potentialSuperType.IsInterface)
        {
            foreach (var interfaceType in thisType.GetInterfaces())
            {
                var tempType = interfaceType.IsGenericType ? interfaceType.GetGenericTypeDefinition() : interfaceType;

                if (tempType == potentialSuperType)
                {
                    return true;
                }
            }
        }

        //
        // do the concrete type checks, iterating up the inheritance chain, as in orignal
        //
        while (thisType != null && thisType != typeof(object))
        {
            var cur = thisType.IsGenericType ? thisType.GetGenericTypeDefinition() : thisType;

            if (potentialSuperType == cur)
            {
                return true;
            }

            thisType = thisType.BaseType;
        }
        return false;
    }

기본적으로 이것은 System.Type의 확장 방법 일뿐입니다. 필자는 "thisType"유형을 구체적 유형으로 의도적으로 제한했습니다. 나는 당신이해야 할 모든 똑똑한 사람들이 효율적이고 다용도 정적 방법으로 그것을 넘어 뜨릴 수 있다고 확신합니다 :) 코드는 대답의 코드가하지 않는 몇 가지 일을합니다

  1. 개방은 일반적인 "확장"까지입니다-상속 (클래스 생각)과 구현 (인터페이스)을 고려하고 있습니다. 메소드 및 매개 변수 이름이이를 더 잘 반영하도록 변경되었습니다.
  2. 입력 null 유효성 검사 (meah)
  3. 동일한 유형의 입력 (클래스 자체를 확장 할 수 없음)
  4. 해당 유형이 인터페이스 인 경우 단락 실행; GetInterfaces ()는 구현 된 모든 인터페이스 (슈퍼 클래스에서 구현 된 인터페이스조차도)를 반환하므로 상속 트리를 올라갈 필요없이 해당 컬렉션을 반복 할 수 있습니다.

나머지는 기본적으로 JaredPar의 코드와 동일합니다

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