Kiểm tra xem một lớp có nguồn gốc từ một lớp chung không


309

Tôi có một lớp chung trong dự án của tôi với các lớp dẫn xuất.

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

public class Test : GenericClass<SomeType>
{
}

Có cách nào để tìm hiểu xem một Typeđối tượng có nguồn gốc từ GenericClassđâu không?

t.IsSubclassOf(typeof(GenericClass<>))

không hoạt động.

Câu trả lời:


430

Hãy thử mã này

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
Đây là một đoạn mã ngọt ngào, tôi phải nói. Việc thực hiện vòng lặp while cũng tránh được hiệu năng đệ quy không cần thiết. Đó là một giải pháp thanh lịch và đẹp đẽ cho một câu hỏi siêu chung.
EnocNRoll - AnandaGopal Pardue

2
Tôi đã thêm phương thức này vào lớp tĩnh ReflectionUtils trong khung công tác của mình và tôi cũng đã điều chỉnh nó như một phương thức mở rộng cho đối tượng bằng cách định nghĩa toCheck trong phương thức như Type toCheck = obj.GetType (); đưa ra "đối tượng này obj" là tham số đầu tiên.
EnocNRoll - AnandaGopal Pardue

11
Vòng lặp while sẽ không bị phá vỡ nếu loại toCheck không phải là một lớp (tức là giao diện). Điều này sẽ gây ra một NullReferenceException.
JD Courtoy

2
Điều này cũng sẽ trả về true nếu toCheck là loại chung mà bạn đang tìm kiếm.
oillio

14
Điều này chỉ hoạt động đối với kế thừa loại cụ thể ... Trường hợp thử nghiệm: bool dự kiến ​​= true; bool Realistic = Program.IsSubgroupOfRawGeneric (typeof (IEnumerable <>), typeof (List <string>)); Assert.AreEqual (dự kiến, thực tế); // thất bại
Bobby

90

(Đăng lại do viết lại lớn)

Câu trả lời mã của JaredPar là tuyệt vời, nhưng tôi có một mẹo sẽ khiến nó không cần thiết nếu các loại chung của bạn không dựa trên các tham số loại giá trị. Tôi đã gác máy tại sao toán tử "là" không hoạt động, vì vậy tôi cũng đã ghi lại kết quả thử nghiệm của mình để tham khảo trong tương lai. Vui lòng nâng cao câu trả lời này để tăng cường hơn nữa sự rõ ràng của nó.

TIỀN BOA:

Nếu bạn chắc chắn rằng việc triển khai GenericClass của bạn kế thừa từ một lớp cơ sở không chung chung như GenericClassBase, bạn có thể hỏi cùng một câu hỏi mà không gặp rắc rối nào như thế này:

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

IsSubgroupOf ()

Thử nghiệm của tôi chỉ ra rằng IsSubgroupOf () không hoạt động trên các loại chung không tham số, chẳng hạn như

typeof(GenericClass<>)

trong khi đó nó sẽ làm việc với

typeof(GenericClass<SomeType>)

Do đó, đoạn mã sau sẽ hoạt động cho mọi dẫn xuất của GenericClass <>, giả sử bạn sẵn sàng thử nghiệm dựa trên Một số loại:

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

Lần duy nhất tôi có thể tưởng tượng rằng bạn muốn kiểm tra bằng GenericClass <> là trong một kịch bản khung trình cắm.


Suy nghĩ về toán tử "là"

Tại thời điểm thiết kế, C # không cho phép sử dụng các tổng quát không tham số vì về cơ bản chúng không phải là một loại CLR hoàn chỉnh tại thời điểm đó. Do đó, bạn phải khai báo các biến chung với các tham số và đó là lý do tại sao toán tử "is" rất mạnh để làm việc với các đối tượng. Ngẫu nhiên, toán tử "is" cũng không thể đánh giá các kiểu chung không tham số.

Toán tử "is" sẽ kiểm tra toàn bộ chuỗi thừa kế, bao gồm các giao diện.

Vì vậy, với một thể hiện của bất kỳ đối tượng nào, phương thức sau đây sẽ thực hiện thủ thuật:

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

Đây là loại dư thừa, nhưng tôi đoán rằng tôi sẽ đi trước và hình dung nó cho mọi người.

Được

var t = new Test();

Các dòng mã sau đây sẽ trả về true:

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

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

bool test3 = IsTypeof<Test>(t);

Mặt khác, nếu bạn muốn một cái gì đó cụ thể cho GenericClass, bạn có thể làm cho nó cụ thể hơn, tôi cho rằng, như thế này:

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

Sau đó, bạn sẽ kiểm tra như thế này:

bool test1 = IsTypeofGenericClass<SomeType>(t);

2
+1 để phân tích và thử nghiệm. Ngoài ra, câu trả lời của bạn rất hữu ích trong trường hợp của tôi.
Guillermo Gutiérrez

3
Cần lưu ý rằng trình biên dịch hoàn toàn hài lòng với .IsSubgroupOf (typeof (GenericClass <>)), nó chỉ không làm những gì bạn muốn.
dùng2880616

2
Nhưng điều gì sẽ xảy ra nếu một số loại không được biết đến vào thời gian biên dịch?
Ryan The Leach

Điểm của toàn bộ cuộc thảo luận là về khi bạn chỉ có một Typeđối tượng.
Jonathan Wood

@JonathanWood đó là lý do tại sao câu trả lời của tôi ở trên là giao dịch với typeofnhà điều hành. Theo các tài liệu: "Toán tử typeof được sử dụng để lấy đối tượng System.Type cho một loại."
EnocNRoll - AnandaGopal Pardue

33

Tôi đã làm việc thông qua một số mẫu này và thấy chúng thiếu trong một số trường hợp. Phiên bản này hoạt động với tất cả các loại khái quát: loại, giao diện và định nghĩa loại.

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

Dưới đây là các bài kiểm tra đơn vị:

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
Tôi hoang mang về phương thức ResolveGenericTypeDefDef. Biến "ShouldUseGenericType" thực sự được gán giá trị: !parent.IsGenericType || parent.GetGenericTypeDefinition() == parent; Vì vậy, bạn thay thế biến đó bằng việc mở rộng câu lệnh if: if (parent.IsGenericType && shouldUseGenericType) và bạn nhận được if (parent.IsGenericType && (!parent.IsGenericType || parent.GetGenericTypeDefinition() == parent)) sau đó giảm xuống if (parent.IsGenericType && parent.GetGenericTypeDefinition() == parent)) parent = parent.GetGenericTypeDefinition();
Michael Blackburn

2
Mà dường như không làm gì cả. Nếu đây là các loại giá trị gần giống với int j = 0; if (j is an int && j == 0) { j=0; } Tôi có được tham chiếu của tôi bằng và giá trị bằng nhau không? Tôi nghĩ rằng chỉ có một phiên bản của mỗi Loại trong bộ nhớ, vì vậy hai biến chỉ đến cùng loại thực tế là chỉ đến cùng một vị trí trong bộ nhớ.
Michael Blackburn

1
@MichaelBlackburn tại chỗ :) tôi đã tái cấu trúc điều này thành: return Parent.IsGenericType? cha mẹ.GetGenericTypeDefDef (): cha mẹ;
AaronHS

3
Nếu nó đã giống như cha mẹ, chỉ cần trả lại nó! và anh ấy gọi GetGenericTypeDef quá nhiều lần. Nó chỉ cần được gọi một lần
AaronHS

1
Xin lỗi, tôi sẽ thêm một bình luận mới bên dưới.
Menno Deij - van Rijswijk

26

Dường như với tôi rằng việc triển khai này hoạt động trong nhiều trường hợp hơn (lớp chung và giao diện có hoặc không có tham số khởi tạo, bất kể số lượng con và tham số):

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

Dưới đây là 70 76 trường hợp thử nghiệm của tôi :

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

Các lớp và giao diện để thử nghiệm:

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
Đây là giải pháp duy nhất làm việc cho tôi. Tôi không thể tìm thấy bất kỳ giải pháp nào khác hoạt động với các lớp có nhiều tham số loại. Cảm ơn bạn.
Connor Clark

1
Tôi thực sự đánh giá cao việc bạn đăng tất cả các trường hợp thử nghiệm. Tôi nghĩ rằng trường hợp 68 và 69 nên sai thay vì đúng, vì bạn có ClassB, ClassA ở bên trái và ClassA, ClassB ở bên phải.
Grax32

Bạn nói đúng, @Grax. Tôi không có thời gian để chỉnh sửa ngay bây giờ, nhưng tôi sẽ cập nhật bài viết của mình ngay khi hoàn thành. Tôi nghĩ rằng việc hiệu chỉnh phải theo phương thức "ConfirmGenericArgument"
Xav987

1
@Grax: Tôi đã tìm thấy một số thời gian để thực hiện chỉnh sửa. Tôi đã thêm lớp ClassB2, tôi đã thay đổi ConfirmGenericArgument và tôi đã thêm một điều khiển theo lệnh gọi của ConfirmGenericArgument. Tôi cũng sửa đổi các trường hợp 68 và 69, và thêm 68-2, 68-3, 68-4, 69-2, 69-3 và 69-4.
Xav987

1
Cảm ơn ngài. +1 cho giải pháp làm việc VÀ số lượng lớn các trường hợp thử nghiệm (ít nhất là rất lớn đối với tôi).
Eldoïr

10

Mã của JaredPar hoạt động nhưng chỉ cho một mức kế thừa. Đối với các mức thừa kế không giới hạn, sử dụng mã sau đây

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
whiletrong JaredPar bao gồm các cấp không giới hạn.
Jay

@jay ... và tránh đệ quy.
Marc L.

1
@MarcL. Điều này sử dụng đệ quy đuôi, do đó, nó là tầm thường đối với trình biên dịch để tối ưu hóa đệ quy ra.
Darhuuk

10

Đây là một phương pháp nhỏ mà tôi đã tạo để kiểm tra xem một đối tượng có nguồn gốc từ một loại cụ thể. Làm việc tuyệt vời cho tôi!

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

Nó có thể là quá mức cần thiết nhưng tôi sử dụng các phương pháp mở rộng như sau. Họ kiểm tra giao diện cũng như các lớp con. Nó cũng có thể trả về loại có định nghĩa chung được chỉ định.

Ví dụ, đối với ví dụ trong câu hỏi, nó có thể kiểm tra giao diện chung cũng như lớp chung. Kiểu trả về có thể được sử dụng GetGenericArgumentsđể xác định rằng loại đối số chung là "Một số loại".

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

Bài đăng tốt Rất vui được chia ra các mối quan tâm trong hai phương pháp.
Wiebe Tijsma

4

Dựa trên câu trả lời xuất sắc ở trên của Fir3rpho3nixx và David Schmitt, tôi đã sửa đổi mã của họ và thêm bài kiểm tra ShouldInheritOrImcellenceTypedGenericInterface (bài cuối cùng).

    /// <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ất cả điều này có thể được thực hiện dễ dàng với linq. Điều này sẽ tìm thấy bất kỳ loại nào là một lớp con của lớp cơ sở chung GenericBaseType.

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

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

Đây là giải pháp duy nhất hiệu quả với tôi. Đơn giản và thanh lịch. Cảm ơn bạn.
lửa lụa

4

Giải pháp đơn giản: chỉ cần tạo và thêm giao diện thứ hai, không chung cho lớp chung:

public interface IGenericClass
{
}

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

Sau đó chỉ cần kiểm tra cho rằng trong bất kỳ cách nào bạn thích sử dụng is, as, IsAssignableFromvv

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

Rõ ràng chỉ có thể nếu bạn có khả năng chỉnh sửa lớp chung (mà OP dường như có), nhưng nó thanh lịch và dễ đọc hơn một chút so với sử dụng phương thức mở rộng khó hiểu.


1
tuy nhiên, chỉ cần kiểm tra xem một cái gì đó thuộc loại IGenericClasssẽ không đảm bảo cho bạn rằng GenericClasshoặc GenericInterfacethực sự được mở rộng hoặc thực hiện. Điều này có nghĩa, trình biên dịch của bạn cũng sẽ không cho phép bạn truy cập bất kỳ thành viên nào của lớp chung.
B12Toaster

4

Đã thêm vào câu trả lời của @ jaredpar, đây là những gì tôi sử dụng để kiểm tra giao diện:

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

Ví dụ:

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

Tốt bổ sung. Đã cho cả bạn và jaredpar một upvote. Nhận xét duy nhất của tôi là bạn đảo ngược vị trí của các loại (từ câu trả lời của jaredpar) vì phương pháp mở rộng. Tôi loại bỏ nó như một phương thức mở rộng và nó đã ném tôi đi một chút. Không phải vấn đề của bạn mà là của tôi. Chỉ muốn cho người tiếp theo đứng lên. Cảm ơn một lần nữa.
Tony

@Tony, cảm ơn vì tiền boa nhưng lần sau Cập nhật câu trả lời.
ngày 5

@vexe, kiểm tra là quan trọng câu trả lời ban đầu của bạn bị hỏng, nó chỉ hoạt động vì bạn đã kiểm tra nó trên một giao diện. Thứ hai, bạn đang lãng phí sức mạnh xử lý bằng cách chạy chức năng này bất kể loại nào.
ngày 5

2

JaredPar,

Điều này không hoạt động với tôi nếu tôi vượt qua typeof (loại <>) là toCheck. Đây là những gì tôi đã thay đổi.

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

Giải pháp của JaredPar thực sự hoạt động typeof(type<>)như là toCheck. Ngoài ra, bạn thực sự cần kiểm tra null như trong giải pháp của JaredPar. Hơn nữa, tôi không biết bạn đang đạt được điều gì khác bằng cách thay thế cur == generictrong giải pháp của anh ấy bằng cách cur.IsGenericType && generic.GetGenericTypeDefinition() == cur.GetGenericTypeDefinition()hạn chế phương pháp của bạn chỉ hoạt động đối với các loại chung chung. Nói cách khác, bất cứ điều gì như thế này đều thất bại với một ngoại lệ:IsSubclassOfRawGeneric(typeof(MyClass), typeof(MyClass<>))
nawfal

2

@EnocNRoll - Câu trả lời của Ananda Gopal rất thú vị, nhưng trong trường hợp một trường hợp không được khởi tạo trước hoặc bạn muốn kiểm tra với một định nghĩa loại chung, tôi đề nghị phương pháp này:

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

và sử dụng nó như:

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

Có bốn trường hợp có điều kiện khi cả hai t(để được kiểm tra) và dnhiều loại generic và hai trường hợp được bao phủ bởi t==dđó là (1) không phải tvà cũng không dlà một định nghĩa chung chung hoặc (2) cả hai trong số đó là định nghĩa chung chung . Các trường hợp còn lại là một trong số chúng là một định nghĩa chung, chỉ khi dđã là một định nghĩa chung, chúng ta mới có cơ hội nói a tlà mộtd nhưng không phải ngược lại.

Nó nên hoạt động với các lớp hoặc giao diện tùy ý mà bạn muốn kiểm tra và trả về những gì như thể bạn kiểm tra một thể hiện của loại đó với istoán tử.


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

0

Bạn có thể thử tiện ích mở rộng này

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

0

đến cuối trò chơi này ... tôi cũng có một hoán vị khác cho câu trả lời của JarodPar.

Đây là Type.IsSubClassOf (Loại) lịch sự của gương phản xạ:

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

từ đó, chúng ta thấy rằng nó không làm bất cứ điều gì quá khó khăn và tương tự như cách tiếp cận lặp đi lặp lại của JaredPar. càng xa càng tốt. đây là phiên bản của tôi (từ chối trách nhiệm: không được kiểm tra kỹ lưỡng, vì vậy hãy biết nếu bạn thấy có vấn đề)

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

về cơ bản, đây chỉ là một phương thức mở rộng cho System.Type - tôi đã làm điều này để cố ý giới hạn Loại "thisType" thành các loại cụ thể, vì cách sử dụng ngay lập tức của tôi là truy vấn LINQ "trong đó" dự đoán đối với các đối tượng Loại. Tôi chắc chắn rằng tất cả những người thông minh ngoài kia có thể đưa nó xuống một phương thức tĩnh hiệu quả, đa mục đích nếu bạn cần :) mã thực hiện một số điều mà mã câu trả lời không

  1. mở nó lên đến "phần mở rộng" chung - tôi đang xem xét tính kế thừa (nghĩ các lớp) cũng như triển khai (giao diện); tên phương thức và tham số được thay đổi để phản ánh tốt hơn điều này
  2. xác thực đầu vào null (meah)
  3. đầu vào cùng loại (một lớp không thể tự mở rộng)
  4. thực hiện ngắn mạch nếu Loại trong câu hỏi là một giao diện; bởi vì GetInterfaces () trả về tất cả các giao diện được triển khai (ngay cả các giao diện được triển khai trong các siêu lớp), bạn có thể chỉ cần lặp qua bộ sưu tập đó mà không phải trèo lên cây thừa kế

phần còn lại về cơ bản giống như mã của JaredPar

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.