Berdasarkan pertanyaan berikut yang diajukan beberapa hari yang lalu di SO: GetType () dan polimorfisme serta membaca jawaban Eric Lippert , saya mulai berpikir jika menjadikan GetType()
tidak virtual benar-benar memastikan bahwa suatu objek tidak dapat berbohong tentangnya Type
.
Secara spesifik, jawaban Eric menyatakan sebagai berikut:
Perancang kerangka tidak akan menambahkan fitur yang sangat berbahaya seperti membiarkan objek berbohong tentang tipenya hanya untuk membuatnya konsisten dengan tiga metode lain pada tipe yang sama.
Sekarang pertanyaannya adalah: dapatkah saya membuat sebuah objek yang benar - benar berbohong tentang tipenya tanpa membuatnya terlihat jelas? Saya mungkin sangat salah di sini dan saya ingin klarifikasi jika itu masalahnya, tetapi pertimbangkan kode berikut:
public interface IFoo
{
Type GetType();
}
Dan dua implementasi berikut dari antarmuka tersebut:
public class BadFoo : IFoo
{
Type IFoo.GetType()
{
return typeof(int);
}
}
public class NiceFoo : IFoo
{
}
Kemudian jika Anda menjalankan program sederhana berikut:
static void Main(string[] args)
{
IFoo badFoo = new BadFoo();
IFoo niceFoo = new NiceFoo();
Console.WriteLine("BadFoo says he's a '{0}'", badFoo.GetType().ToString());
Console.WriteLine("NiceFoo says he's a '{0}'", niceFoo.GetType().ToString());
Console.ReadLine();
}
Benar saja badFoo
keluaran yang salah Type
.
Sekarang saya tidak tahu apakah ini memiliki implikasi serius berdasarkan Eric yang menggambarkan perilaku ini sebagai " fitur yang sangat berbahaya ", tetapi dapatkah pola ini menimbulkan ancaman yang dapat dipercaya?
IFoo.GetType
danobject.GetType
bukan hal yang sama jadi tidak ada hal buruk yang terjadi di sini kecuali gaya yang buruk. Sunting: UmumnyaGetType
akan dipanggil pada beberapa objek yang tidak dikenal pada waktu kompilasi, dalam banyak kasusobject
dan bukan antarmuka yang cerdik. :)GetType
, dengan tanda tangan yang sama. Itu tidak berhubungan denganGetType
metode yang penting. Anda juga bisa membuat metode contoh publik yang menyembunyikan metode yang relevanGetType
, menggunakannew
kata kunci pengubah. Catatan, jika Anda memiliki metode umum sepertistatic Type Test<T>(T t) { return t.GetType(); }
(tanpa batasan aktifT
), hal-hal seperti ituTest<IFoo>(new BadFoo())
akan tetap memanggilGetType
metode asli .Jawaban:
Pertanyaan bagus! Menurut saya, Anda hanya bisa menyesatkan sesama pengembang jika GetType adalah objek virtual, padahal sebenarnya bukan.
Apa yang Anda lakukan mirip dengan membayangi GetType, seperti ini:
dengan kelas ini (dan menggunakan kode sampel dari MSDN untuk metode GetType () ) Anda memang dapat memiliki:
Jadi, yikes, kamu sudah berhasil berbohong, bukan? Ya dan tidak ... Pertimbangkan bahwa menggunakan ini sebagai eksploitasi berarti menggunakan instance BadFoo Anda sebagai argumen ke metode di suatu tempat, yang mengharapkan kemungkinan sebuah
object
atau tipe dasar umum untuk hierarki objek. Sesuatu seperti ini:tapi
CheckIfInt(foo)
mencetak "bukan int".Jadi, pada dasarnya (kembali ke contoh Anda), Anda benar-benar hanya dapat mengeksploitasi "tipe berbohong" Anda dengan kode yang ditulis seseorang di
IFoo
antarmuka Anda , yang sangat eksplisit tentang fakta bahwa ia memiliki metode "kustom"GetType()
.Hanya jika GetType () adalah virtual pada objek Anda akan dapat membuat tipe "berbohong" yang dapat digunakan dengan metode seperti di
CheckIfInt
atas untuk membuat kekacauan di perpustakaan yang ditulis oleh orang lain.sumber
Ada dua cara untuk memastikan Type:
Gunakan
typeof
pada Jenis yang tidak dapat dibebaniTransmisikan instance ke
object
dan panggilGetType()
Metodesumber
typeof
metode yang hanya mendapatIFoo badFoo
parameter sebagai?typeof
tidak dapat diterapkan ke instance kelas yang perlu kita lakukan di sini. Satu-satunya pilihan Anda adalahGetType()
.BadFoo
" tetapi tidak "mendapatkan tipe variabelbadFoo
."Tidak, Anda tidak bisa membuat GetType berbohong. Anda hanya memperkenalkan metode baru. Hanya kode yang mengetahui metode ini yang akan memanggilnya.
Misalnya, Anda tidak dapat membuat kode pihak ketiga atau kerangka kerja memanggil metode GetType baru Anda alih-alih yang asli, karena kode itu tidak tahu bahwa metode Anda ada dan karena itu tidak akan pernah memanggilnya.
Namun Anda dapat membingungkan developer Anda sendiri dengan deklarasi seperti itu. Kode apa pun yang dikompilasi dengan deklarasi Anda dan yang menggunakan parameter atau variabel yang diketik sebagai IFoo atau tipe turunannya memang akan menggunakan metode baru Anda. Tapi karena itu hanya mempengaruhi kode Anda sendiri, itu tidak benar-benar menimbulkan "ancaman".
Jika Anda ingin memberikan deskripsi tipe kustom untuk kelas, ini harus dilakukan menggunakan Custom Type Descriptor , mungkin dengan memberi anotasi kelas Anda dengan TypeDescriptionProviderAttribute . Ini dapat berguna dalam beberapa situasi.
sumber
Nah, sebenarnya ada adalah sudah jenis yang dapat berbaring di
GetType
: setiap jenis nullable.Kode ini :
keluaran
True
.Sebenarnya bukan
int?
siapa yang bohong, hanya pemeran implisit yangobject
berubahint?
menjadi kotakint
. Namun demikian Anda tidak bisa mengatakanint?
dariint
denganGetType()
.sumber
GetType()
menghasilkan hasil yang agak aneh. Saya sebenarnya telah bertanya kepada beberapa rekan tentang apakah non-shadowedGetType()
dapat mengembalikan sesuatu yang berbeda dari tipe runtime objek sebenarnya, jawaban semua orang adalah 'tidak'.Saya tidak berpikir itu akan terjadi, karena setiap kode perpustakaan yang memanggil GetType akan mendeklarasikan variabel sebagai 'Object' atau sebagai tipe Generik 'T'
Kode berikut:
cetakan:
Satu-satunya saat kode semacam ini akan menghasilkan skenario buruk adalah di kode Anda sendiri, di mana Anda mendeklarasikan jenis parameter sebagai IFoo
sumber
Hal terburuk yang bisa terjadi sejauh yang saya tahu adalah menyesatkan programmer yang tidak bersalah yang kebetulan menggunakan kelas keracunan, misalnya:
Jika
myInstance
adalah instance dari kelas seperti yang Anda gambarkan dalam pertanyaan, itu hanya akan diperlakukan sebagai tipe yang tidak diketahui.Jadi jawaban saya adalah tidak, tidak dapat melihat ancaman nyata di sini.
sumber
Object.GetType()
berbeda dariSomeUserdefinedInterfaceClassOrStruct.GetType()
. Hanya, jika Anda menggunakandynamic
tipe tersebut, Anda tidak akan pernah tahu apa yang akan terjadi pada waktu pengikatan. Jadi sebaiknya gunakandynamic x = expression; ... Type t = ((object)x).GetType();
dalam kasus seperti itu.Anda memiliki beberapa opsi jika Anda ingin bermain aman melawan peretasan semacam itu:
Transmisikan ke objek terlebih dahulu
Anda dapat memanggil
GetType()
metode asli dengan terlebih dahulu mentransmisikan instance keobject
:menghasilkan:
Gunakan metode template
Menggunakan metode templat ini juga akan memberi Anda tipe sebenarnya:
sumber
Ada perbedaan antara
object.GetType
danIFoo.GetType
.GetType
dipanggil pada waktu kompilasi pada objek yang tidak dikenal, bukan pada Antarmuka. Dalam contoh Anda, dengan keluaranbadFoo.GetType
itu diharapkan lebih baik, karena Anda membebani metode. Hanya masalahnya, programmer lain bisa bingung dengan perilaku ini.Tetapi jika Anda menggunakannya,
typeof()
itu akan menghasilkan bahwa jenisnya sama, dan Anda tidak dapat menimpatypeof()
.Juga programmer dapat melihat pada waktu kompilasi, metode mana yang
GetType
dia panggil.Jadi untuk pertanyaan Anda: Pola ini tidak dapat menimbulkan ancaman yang kredibel, tetapi juga bukan gaya pengkodean terbaik.
sumber
badFoo.GetType()
Perilaku yang diharapkan IS, karenaGetType
kelebihan beban.