Bir sınıfın genel bir sınıftan türetilmiş olup olmadığını kontrol edin


309

Projemde türetilmiş sınıflarla genel bir sınıf var.

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

public class Test : GenericClass<SomeType>
{
}

Bir Typenesneden türetilmiş olup olmadığını öğrenmenin bir yolu var mı GenericClass?

t.IsSubclassOf(typeof(GenericClass<>))

çalışmıyor.

Yanıtlar:


430

Bu kodu deneyin

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
Bu tatlı bir kod parçası, söylemeliyim. While döngüsü uygulaması, gereksiz özyineleme performans isabetini de önler. Meta-jenerik bir soru için zarif ve güzel bir çözüm.
EnocNRoll - AnandaGopal Pardue

2
Bu yöntemi benim çerçevemdeki ReflectionUtils statik sınıfına ekledim ve ayrıca Type toCheck = obj.GetType (); verilen "bu nesne obj" ilk parametredir.
EnocNRoll - AnandaGopal Pardue

11
ToCheck türü bir sınıf (ör. Arabirim) değilse while döngüsü kesilmez. Bu bir NullReferenceException özelliğine neden olur.
JD Courtoy

2
Eğer toCheck aradığınız genel tip ise bu da true değerini döndürecektir.
oillio

14
Bu sadece somut tip kalıtım için geçerlidir ... Test durumu: bool beklenen = doğru; bool actual = Program.IsSubclassOfRawGeneric (typeof (IEnumerable <>), typeof (List <string>)); Assert.AreEqual (beklenen, gerçek); // başarısız
Bobby

90

(Büyük bir yeniden yazma nedeniyle yeniden yayınlandı)

JaredPar'ın kod yanıtı harika, ancak genel türleriniz değer türü parametrelerine dayanmıyorsa gereksiz hale getirecek bir ipucum var. "Neden" operatörünün işe yaramayacağına karar verdim, bu yüzden ileride başvurmak üzere deneyimin sonuçlarını da belgeledim. Netliğini daha da artırmak için lütfen bu cevabı geliştirin.

İPUCU:

GenericClass uygulamanızın, GenericClassBase gibi soyut bir genel olmayan temel sınıftan miras aldığından emin olursanız, aynı soruyu şu şekilde sorunsuz bir şekilde sorabilirsiniz:

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

IsSubclassOf ()

Benim test IsSubclassOf () gibi parametresiz jenerik türleri üzerinde çalışmadığını gösterir

typeof(GenericClass<>)

oysa birlikte çalışacak

typeof(GenericClass<SomeType>)

Bu nedenle, aşağıdaki kod, SomeType'a dayalı olarak test etmek istediğinizi varsayarsak, GenericClass <> türetilmesinde işe yarar:

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

GenericClass <> ile test etmek istediğinizi hayal edebileceğim tek zaman eklenti çerçeve senaryosunda.


"İs" operatörü hakkındaki düşünceler

Tasarım zamanında C # parametresiz jeneriklerin kullanımına izin vermez, çünkü bunlar aslında tam bir CLR tipi değildir. Bu nedenle, genel değişkenleri parametrelerle bildirmeniz gerekir ve bu nedenle "is" operatörü nesnelerle çalışmak için çok güçlüdür. Bu arada, "is" operatörü parametresiz jenerik tiplerini de değerlendiremez.

"İs" operatörü, arabirimler dahil tüm kalıtım zincirini test edecektir.

Bu nedenle, herhangi bir nesnenin bir örneği verildiğinde, aşağıdaki yöntem hile yapar:

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

Bu bir çeşit gereksiz, ama devam edip herkes için görselleştireceğimi düşündüm.

verilmiş

var t = new Test();

Aşağıdaki kod satırları true değerini döndürür:

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

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

bool test3 = IsTypeof<Test>(t);

Öte yandan, GenericClass'a özgü bir şey istiyorsanız, bunu daha spesifik hale getirebilirsiniz, sanırım, şöyle:

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

O zaman şöyle test edersiniz:

bool test1 = IsTypeofGenericClass<SomeType>(t);

2
Analiz ve test için +1. Ayrıca, cevabım benim durumumda çok faydalı oldu.
Guillermo Gutiérrez

3
Derleyicinin .IsSubclassOf (typeof (GenericClass <>)) ile mükemmel bir şekilde mutlu olduğu unutulmamalıdır, sadece istediğinizi yapmaz.
user2880616

2
Peki, SomeType derleme zamanında bilinmiyorsa ne olur?
Ryan The Leach

Tüm tartışmanın Typeamacı sadece bir nesneniz olduğundaydı.
Jonathan Wood

@JonathanWood bu yüzden yukarıdaki cevabım typeofoperatörle ilgileniyor . Dokümanlara göre: "typeof operatörü bir tür için System.Type nesnesini elde etmek için kullanılır."
EnocNRoll - AnandaGopal Pardue

33

Bu örneklerin bazılarında çalıştım ve bazı durumlarda bunların eksik olduğunu gördüm. Bu versiyon her türlü jenerik ile çalışır: tipler, arayüzler ve bunların tip tanımları.

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;
}

Birim testleri de şöyledir:

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 yöntemi hakkında şaşkınım. "ShouldUseGenericType" değişkenine gerçekten değer atanır: !parent.IsGenericType || parent.GetGenericTypeDefinition() == parent; Bu nedenle, bu değişkeni if ​​ifadesinin genişlemesi ile değiştirirsiniz: Daha sonra if (parent.IsGenericType && shouldUseGenericType) bunu alırsınız if (parent.IsGenericType && (!parent.IsGenericType || parent.GetGenericTypeDefinition() == parent))if (parent.IsGenericType && parent.GetGenericTypeDefinition() == parent)) parent = parent.GetGenericTypeDefinition();
Michael Blackburn

2
Hiçbir şey yapmıyor gibi görünüyor. Bunlar benzer olurdu değer türleri olsaydı int j = 0; if (j is an int && j == 0) { j=0; } ben benim referans eşit ve değer eşit karışık mı? Bellekte her Tür sadece tek bir örnek olduğunu düşündüm, bu yüzden aynı tür işaret iki değişken aslında bellekte aynı konuma işaret ediyor.
Michael Blackburn

1
@MichaelBlackburn nokta :) ben sadece olmak için refactored: dönüş parent.IsGenericType? parent.GetGenericTypeDefinition (): parent;
AaronHS

3
zaten ebeveyn ile aynı ise, sadece iade edin! ve GetGenericTypeDef'i çok fazla çağırıyor. Sadece bir kez çağrılması gerekiyor
AaronHS

1
Üzgünüm, aşağıya yeni bir yorum ekleyeceğim.
Menno Deij - van Rijswijk

26

Bana öyle geliyor ki bu uygulama daha fazla durumda çalışır (alt sınıf ve parametre sayısına bakılmaksızın başlatılan parametrelerle veya başlamasız genel sınıf ve arabirim):

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;
    }
}

İşte benim 70 76 test durumlarım:

[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");
}

Test için sınıflar ve arayüzler:

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
Benim için işe yarayan tek çözüm bu. Birden çok tip parametresi olan sınıflarla çalışan başka bir çözüm bulamadım. Teşekkür ederim.
Connor Clark

1
Tüm bu test senaryolarını gönderdiğiniz için teşekkür ederiz. Solda ClassB, ClassA ve sağda ClassA, ClassB olduğu için 68 ve 69 numaralı vakaların doğru yerine yanlış olması gerektiğini düşünüyorum.
Grax32

Haklısın @Grax. Şimdi düzeltmeyi yapmak için zamanım yok, ancak yayınımı yapılır yapılmaz güncelleyeceğim. Bence düzeltme "VerifyGenericArguments" yönteminde olmalı
Xav987

1
@Grax: Düzeltmeyi yapmak için biraz zaman buldum. ClassB2 sınıfını ekledim, VerifyGenericArguments değiştirdim ve VerifyGenericArguments çağrısında bir denetim ekledim. Ayrıca 68 ve 69 numaralı vakaları değiştirdim ve 68-2, 68-3, 68-4, 69-2, 69-3 ve 69-4'ü ekledim.
Xav987

1
Teşekkürler bayım. Çalışma çözümü için +1 ve muazzam miktarda test vakası (en azından benim için muazzam).
Eldoïr

10

JaredPar'ın kodu çalışır, ancak yalnızca bir düzey kalıtım için. Sınırsız kalıtım düzeyi için aşağıdaki kodu kullanın

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 kodunda sınırsız seviyelerini kapsar.
Jay

@jay ... ve özyinelemeden kaçınır.
Marc L.

1
@MarcL. Bu, kuyruk özyinelemesini kullanır, bu nedenle derleyicinin özyinelemeyi optimize etmesi önemsiz olmalıdır.
Darhuuk

10

İşte bir nesnenin belirli bir türden türetildiğini kontrol etmek için oluşturduğum küçük bir yöntem. Benim için harika çalışıyor!

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

Aşırı olabilir ama aşağıdaki gibi uzatma yöntemleri kullanıyorum. Arabirimleri ve alt sınıfları kontrol ederler. Ayrıca, belirtilen genel tanıma sahip türü de döndürebilir.

Örneğin, sorudaki örnek için, genel sınıfın yanı sıra genel arabirime karşı da test edebilir. Döndürülen tür GetGenericArguments, genel bağımsız değişken türünün "SomeType" olduğunu belirlemek için ile kullanılabilir .

/// <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;
}

İyi yazı! Endişeleri iki yöntemle ayırmak güzel.
Wiebe Tijsma

4

Fir3rpho3nixx ve David Schmitt tarafından yukarıdaki mükemmel cevaba dayanarak, kodlarını değiştirdim ve ShouldInheritOrImplementTypedGenericInterface testini (sonuncusu) ekledim.

    /// <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

Tüm bunlar linq ile kolayca yapılabilir. Bu, GenericBaseType genel temel sınıfının alt sınıfı olan tüm türleri bulur.

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

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

Benim için işe yarayan tek çözüm buydu. Basit ve zarif. Teşekkür ederim.
silkfire

4

Basit çözüm: Genel sınıfa ikinci, jenerik olmayan bir arayüz oluşturun ve ekleyin:

public interface IGenericClass
{
}

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

Sonra sadece herhangi bir şekilde kullanmakta seviyorum o kontrol is, as, IsAssignableFromvb

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

Açıkçası sadece (OP'nin sahip olduğu gibi) genel sınıfı düzenleme yeteneğiniz varsa, ancak şifreli bir uzatma yöntemi kullanmaktan biraz daha zarif ve okunabilir.


1
Ancak bir şey türünde olup olmadığı kontrol IGenericClasssize garanti vermez GenericClassveya GenericInterfacefiilen uzatılmış veya uygulanmaktadır. Bu, derleyicinizin genel sınıfın herhangi bir üyesine erişmenize izin vermeyeceği anlamına gelir.
B12Toaster

4

@ Jaredpar'ın cevabına eklendi, arayüzleri kontrol etmek için kullandığım şey:

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);
}

Ör:

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

İyi bir ek. Hem size hem de jaredpar'a bir oy verin. Benim tek yorumum, uzatma yöntemi nedeniyle türlerin (jaredpar'ın cevabından) konumunu tersine çevirdiğinizdir. Bir uzantı yöntemi olarak kaldırdım ve beni biraz attı. Senin problemin değil benimki. Sadece bir sonraki kişiye kafayı vermek istedim. Tekrar teşekkürler.
Tony

@Tony, bahşiş için teşekkürler ama bir dahaki sefere Cevabı güncelleyin.
johnny 5

@vexe, orijinal cevabınızın kırılması test önemlidir, sadece bir arayüzde test ettiğiniz için çalışır. İkincisi, türden bağımsız olarak bu işlevi çalıştırarak işlem gücünü boşa harcıyoruz.
johnny 5

2

JaredPar,

ToCheck olarak typeof (type <>) iletirsem bu benim için çalışmadı. İşte ben değiştirdim.

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 çözümü gerçekten de çalışıyor typeof(type<>)olarak toCheck. Ayrıca gerçekten JaredPar'ın çözümünde olduğu gibi null kontrole ihtiyacınız var. Ayrıca, cur == genericçözümünüzü cur.IsGenericType && generic.GetGenericTypeDefinition() == cur.GetGenericTypeDefinition()yalnızca genel türler için çalışacak şekilde kısıtlamaktan başka bir çözümle değiştirerek başka ne elde ettiğinizi bilmiyorum . Başka bir deyişle, böyle bir şey bir istisna dışında başarısız olur:IsSubclassOfRawGeneric(typeof(MyClass), typeof(MyClass<>))
nawfal

2

@EnocNRoll - Ananda Gopal'ın cevabı ilginç, ancak bir örnek önceden başlatılmamışsa veya genel bir tür tanımıyla kontrol etmek istiyorsanız, bu yöntemi öneririm:

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;
}

ve şöyle kullanın:

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

Orada her iki durumda, dört şartlı durumlardır t(test edilecek) ve dgenel tipleri ve iki olgu kapsamındadır ile t==dki bunlar (1) ne tde dgenel bir tanım veya (2) her ikisi de genel tanımları bulunmaktadır . Dinlenme vakalar bunlardan biri sadece genel bir tanımıdır vardır dzaten dediğimiz şansına sahip genel tanım bir tbir olduğunud ama tersi.

Test etmek istediğiniz rasgele sınıflar veya arabirimlerle çalışmalı ve bu türden bir örneği işleçle test ediyormuşsunuz gibi döndürür is.


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

0

Bu uzantıyı deneyebilirsiniz

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

0

Bu oyuna geç ... Ben de JarodPar'ın cevabına başka bir permütasyon daha var.

reflektörün izniyle 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;
    }

bundan dolayı, çok cray cray işi yapmadığını ve JaredPar'ın yinelemeli yaklaşımına benzediğini görüyoruz. çok uzak çok iyi. İşte benim sürümüm (feragatname: ayrıntılı olarak test edilmemiştir, bu yüzden sorunları bulursanız bana bildirin)

    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;
    }

temelde bu sadece System.Type için bir uzatma yöntemidir - Ben hemen benim türü LINQ sorgusu "nerede" Type nesnelerine tahmin olduğu gibi bu tür "thisType" Türü somut Türleri sınırlamak için bunu yaptı. eminim orada tüm akıllı millet verimli, çok amaçlı statik bir yöntem aşağı gerekiyorsa :) :) kod cevap cevap birkaç şey yapar eminim

  1. open's genel "uzantısı" kadar - ben miras (düşünüyorum sınıfları) yanı sıra uygulama (arayüzler) düşünüyorum; yöntem ve parametre adları bunu daha iyi yansıtacak şekilde değiştirilir
  2. giriş null-doğrulama (evet)
  3. aynı türde girdi (bir sınıf kendini genişletemez)
  4. söz konusu Tür bir arabirim ise kısa devre yürütme; GetInterfaces () tüm uygulanmış arabirimleri (süper sınıflarda uygulananlar bile) döndürdüğünden, miras ağacına tırmanmak zorunda kalmadan bu koleksiyonda dolaşabilirsiniz

gerisi temelde JaredPar'ın kodu ile aynı

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.