Bagaimana cara saya memeriksa apakah suatu jenis subtipe ATAU jenis suatu objek?

335

Untuk memeriksa apakah suatu jenis adalah subkelas dari jenis lain dalam C #, mudah:

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

Namun, ini akan gagal:

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

Apakah ada cara untuk memeriksa apakah suatu tipe adalah subkelas ATAU dari kelas dasar itu sendiri, tanpa menggunakan ORoperator atau menggunakan metode ekstensi?

Daniel T.
sumber

Jawaban:

499

Rupanya tidak.

Berikut opsinya:

Type.IsSubclassOf

Seperti yang sudah Anda ketahui, ini tidak akan berfungsi jika kedua jenisnya sama, inilah contoh program LINQPad yang menunjukkan:

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

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

Keluaran:

True
False

Yang menunjukkan bahwa itu Derivedadalah subkelas dari Base, tetapi itu Base(jelas) bukan subkelas dari itu sendiri.

Type.IsAssignableFrom

Sekarang, ini akan menjawab pertanyaan khusus Anda, tetapi itu juga akan memberi Anda positif palsu. Seperti yang ditunjukkan oleh Eric Lippert dalam komentar, sementara metode tersebut memang akan kembali Trueuntuk dua pertanyaan di atas, itu juga akan kembali Trueuntuk ini, yang mungkin tidak Anda inginkan:

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

Di sini Anda mendapatkan output berikut:

True
True
True

Terakhir Trueada akan menunjukkan, jika metode hanya menjawab pertanyaan yang diajukan, yang uint[]mewarisi dari int[]atau bahwa mereka adalah tipe yang sama, yang jelas bukan itu masalahnya.

Jadi IsAssignableFromtidak sepenuhnya benar juga.

is dan as

"Masalah" dengan isdan asdalam konteks pertanyaan Anda adalah bahwa mereka akan mengharuskan Anda untuk beroperasi pada objek dan menulis salah satu jenis secara langsung dalam kode, dan tidak bekerja dengan Typeobjek.

Dengan kata lain, ini tidak dapat dikompilasi:

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

ini juga tidak akan:

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

ini juga tidak akan:

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

Kesimpulan

Meskipun metode di atas mungkin sesuai dengan kebutuhan Anda, satu-satunya jawaban yang benar untuk pertanyaan Anda (seperti yang saya lihat) adalah bahwa Anda akan memerlukan pemeriksaan tambahan:

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

yang tentu saja lebih masuk akal dalam suatu metode:

public bool IsSameOrSubclass(Type potentialBase, Type potentialDescendant)
{
    return potentialDescendant.IsSubclassOf(potentialBase)
           || potentialDescendant == potentialBase;
}
Lasse V. Karlsen
sumber
2
Terima kasih! Saya akan menandai ini sebagai jawaban yang benar (harus menunggu 8 menit lagi) karena Anda menyebutkan bahwa cek harus dibalik dan memberikan tautan ke dokumentasi MSDN.
Daniel T.
71
Perhatikan bahwa ini sebenarnya tidak melakukan apa yang diminta pertanyaan; ini tidak menentukan apakah satu jenis adalah subkelas dari yang lain, melainkan apakah satu jenis adalah penugasan yang kompatibel dengan yang lain. Array uint bukan subkelas dari array int, tetapi mereka kompatibel dengan penugasan. IEnumerable <Giraffe> bukan subkelas dari IEnumerable <Animal>, tetapi mereka adalah tugas yang kompatibel di v4.
Eric Lippert
Tidak ada cara untuk melakukan ini dalam nama metode seperti Java? `` `batal <? extends Base> saveObject (? objectToSave) `` `
Oliver Dixon
2
Bagaimana IsInstanceOfTypecocok dengan ini?
Lennart
Mungkin tergoda untuk mengubah IsSameOrSubclass menjadi metode ekstensi, tetapi saya sarankan untuk tidak melakukannya, agak canggung untuk membaca dan menulis, dan mudah berantakan (membalikkan urutan potensial Base dan potensialDescendant bisa mematikan).
jrh
36
typeof(BaseClass).IsAssignableFrom(unknownType);
Marc Gravell
sumber
1

Jika Anda mencoba melakukannya di proyek PCL Xamarin Forms, solusi di atas menggunakan IsAssignableFrommemberikan kesalahan:

Galat: 'Tipe' tidak mengandung definisi untuk 'IsAssignableFrom' dan tidak ada metode ekstensi 'IsAssignableFrom' menerima argumen pertama dari tipe 'Type' dapat ditemukan (apakah Anda kehilangan arahan menggunakan atau referensi rakitan?)

karena IsAssignableFrommeminta TypeInfoobjek. Anda dapat menggunakan GetTypeInfo()metode ini dari System.Reflection:

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

Bruno Serrano
sumber
0

Saya memposting jawaban ini dengan harapan seseorang berbagi dengan saya jika dan mengapa itu adalah ide yang buruk. Dalam aplikasi saya, saya memiliki properti Tipe yang ingin saya periksa untuk memastikan itu adalah typeof (A) atau typeof (B), di mana B adalah kelas yang berasal dari A. Jadi kode saya:

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

Saya merasa mungkin agak naif di sini sehingga umpan balik akan diterima.

baskren
sumber
2
Ada beberapa masalah dengan ide itu: 1) tipe membutuhkan konstruktor tanpa parameter atau CreateInstance akan gagal; 2) casting ke (A) tidak mengembalikan null jika pemain tidak dapat dibuat, itu melempar; 3) Anda tidak benar-benar membutuhkan instance baru, sehingga Anda memiliki alokasi yang tidak berguna. Jawaban yang diterima lebih baik (meskipun tidak sempurna).
Marcel Popescu
Terima kasih untuk umpan baliknya. Sangat membantu.
baskren
@ Baskren, mungkin kemudian upvote komentarnya yang bermanfaat. Saya mendapat dukungan # 1.
Developer63