Làm cách nào để kiểm tra xem một loại có phải là kiểu con HOẶC loại đối tượng không?


335

Để kiểm tra xem một loại có phải là lớp con của loại khác trong C # hay không, thật dễ dàng:

typeof (SubClass).IsSubclassOf(typeof (BaseClass)); // returns true

Tuy nhiên, điều này sẽ thất bại:

typeof (BaseClass).IsSubclassOf(typeof (BaseClass)); // returns false

Có cách nào để kiểm tra xem một loại có phải là lớp con HOẶC của chính lớp cơ sở đó không mà không sử dụng ORtoán tử hoặc sử dụng phương thức mở rộng?

Câu trả lời:


498

Rõ ràng là không.

Đây là các tùy chọn:

Loại.IsSubgroupOf

Như bạn đã phát hiện ra, điều này sẽ không hoạt động nếu hai loại giống nhau, đây là một chương trình LINQPad mẫu minh họa:

void Main()
{
    typeof(Derived).IsSubclassOf(typeof(Base)).Dump();
    typeof(Base).IsSubclassOf(typeof(Base)).Dump();
}

public class Base { }
public class Derived : Base { }

Đầu ra:

True
False

Điều đó chỉ ra rằng đó Derivedlà một lớp con của Base, nhưng đó Base(rõ ràng) không phải là một lớp con của chính nó.

Loại.IsAssignableFrom

Bây giờ, điều này sẽ trả lời câu hỏi cụ thể của bạn, nhưng nó cũng sẽ cung cấp cho bạn dương tính giả. Như Eric Lippert đã chỉ ra trong các bình luận, trong khi phương thức thực sự sẽ trả về Truehai câu hỏi trên, nó cũng sẽ trả về Truenhững điều này, điều mà bạn có thể không muốn:

void Main()
{
    typeof(Base).IsAssignableFrom(typeof(Derived)).Dump();
    typeof(Base).IsAssignableFrom(typeof(Base)).Dump();
    typeof(int[]).IsAssignableFrom(typeof(uint[])).Dump();
}

public class Base { }
public class Derived : Base { }

Ở đây bạn nhận được đầu ra sau:

True
True
True

Cuối cùng Trueở đó sẽ chỉ ra, nếu phương thức chỉ trả lời câu hỏi được hỏi, đó là uint[]kế thừa từ int[]hoặc chúng cùng loại, rõ ràng không phải là trường hợp.

Vì vậy, IsAssignableFromcũng không hoàn toàn chính xác.

isas

"Vấn đề" với isastrong bối cảnh câu hỏi của bạn là họ sẽ yêu cầu bạn thao tác trên các đối tượng và viết một trong các loại trực tiếp trong mã, và không hoạt động với Typecác đối tượng.

Nói cách khác, điều này sẽ không được biên dịch:

SubClass is BaseClass
^--+---^
   |
   +-- need object reference here

cũng không phải điều này:

typeof(SubClass) is typeof(BaseClass)
                    ^-------+-------^
                            |
                            +-- need type name here, not Type object

cũng không phải điều này:

typeof(SubClass) is BaseClass
^------+-------^
       |
       +-- this returns a Type object, And "System.Type" does not
           inherit from BaseClass

Phần kết luận

Mặc dù các phương pháp trên có thể phù hợp với nhu cầu của bạn, nhưng câu trả lời đúng duy nhất cho câu hỏi của bạn (như tôi thấy) là bạn sẽ cần kiểm tra thêm:

typeof(Derived).IsSubclassOf(typeof(Base)) || typeof(Derived) == typeof(Base);

điều này tất nhiên có ý nghĩa hơn trong một phương thức:

public bool IsSameOrSubclass(Type potentialBase, Type potentialDescendant)
{
    return potentialDescendant.IsSubclassOf(potentialBase)
           || potentialDescendant == potentialBase;
}

2
Cảm ơn! Tôi sẽ đánh dấu đây là câu trả lời đúng (phải chờ thêm 8 phút nữa) vì bạn đã đề cập rằng kiểm tra phải được đảo ngược và cung cấp một liên kết đến tài liệu MSDN.
Daniel T.

71
Lưu ý rằng điều này không thực sự làm những gì câu hỏi yêu cầu; điều này không xác định liệu một loại có phải là một lớp con của một loại khác hay không, mà là liệu một loại có tương thích với một loại khác hay không. Một mảng uint không phải là một lớp con của một mảng int, nhưng chúng tương thích với nhiệm vụ. IEnumerable <Gi hươu cao cổ> không phải là một lớp con của IEnumerable <Animal>, nhưng chúng tương thích với nhiệm vụ trong v4.
Eric Lippert

Không có cách nào để làm điều này trong tên phương thức như Java? `` `void <? mở rộng Base> saveObject (? objectToSave) `` `
Oliver Dixon

2
Làm thế nào sẽ IsInstanceOfTypephù hợp với điều này?
Lennart

Việc chuyển đổi IsSameOrSub class thành một phương thức mở rộng có thể rất hấp dẫn nhưng tôi khuyên bạn nên chống lại nó, hơi khó đọc và dễ gây rối (đảo ngược thứ tự củaentialBase và tiềm năng có thể gây chết người).
jrh



1

Nếu bạn đang cố gắng thực hiện nó trong dự án PCL Xamarin Forms, các giải pháp trên sử dụng IsAssignableFromsẽ gây ra lỗi:

Lỗi: 'Loại' không chứa định nghĩa cho 'IsAssignableFrom' và không có phương thức mở rộng 'IsAssignableFrom' chấp nhận đối số đầu tiên của loại 'Loại' (bạn có thiếu tham chiếu sử dụng chỉ thị hoặc tham chiếu lắp ráp không?)

bởi vì IsAssignableFromyêu cầu một TypeInfođối tượng Bạn có thể sử dụng GetTypeInfo()phương pháp từ System.Reflection:

typeof(BaseClass).GetTypeInfo().IsAssignableFrom(typeof(unknownType).GetTypeInfo())


0

Tôi đang đăng câu trả lời này với hy vọng ai đó chia sẻ với tôi nếu và tại sao nó sẽ là một ý tưởng tồi. Trong ứng dụng của mình, tôi có một thuộc tính Loại mà tôi muốn kiểm tra để chắc chắn rằng đó là typeof (A) hoặc typeof (B), trong đó B là bất kỳ lớp nào có nguồn gốc từ A. Vì vậy, mã của tôi:

public class A
{
}

public class B : A
{
}

public class MyClass
{
    private Type _helperType;
    public Type HelperType
    {
        get { return _helperType; }
        set 
        {
            var testInstance = (A)Activator.CreateInstance(value);
            if (testInstance==null)
                throw new InvalidCastException("HelperType must be derived from A");
            _helperType = value;
        }
    }
}

Tôi cảm thấy mình có thể hơi ngây thơ ở đây nên mọi phản hồi đều được hoan nghênh.


2
Có một vài vấn đề với ý tưởng đó: 1) kiểu cần một hàm tạo không tham số hoặc CreatInstance sẽ thất bại; 2) truyền tới (A) không trả về null nếu diễn viên không thể thực hiện được, nó ném; 3) bạn không thực sự cần phiên bản mới, vì vậy bạn có một sự phân bổ vô dụng. Câu trả lời được chấp nhận là tốt hơn (mặc dù không hoàn hảo).
Marcel Popescu

Cảm ơn vì bạn đã phản hồi. Rất hữu ích.
baskren

@baskren, có lẽ sau đó upvote bình luận hữu ích của mình. Tôi đã nâng cấp # 1.
Nhà phát triển63
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.