Ketika pemrograman antarmuka, saya telah menemukan saya melakukan banyak casting atau konversi tipe objek.
Apakah ada perbedaan antara kedua metode konversi ini? Jika demikian, apakah ada perbedaan biaya atau bagaimana hal ini memengaruhi program saya?
public interface IMyInterface
{
void AMethod();
}
public class MyClass : IMyInterface
{
public void AMethod()
{
//Do work
}
// Other helper methods....
}
public class Implementation
{
IMyInterface _MyObj;
MyClass _myCls1;
MyClass _myCls2;
public Implementation()
{
_MyObj = new MyClass();
// What is the difference here:
_myCls1 = (MyClass)_MyObj;
_myCls2 = (_MyObj as MyClass);
}
}
Juga, apa yang "secara umum" metode yang disukai?
Jawaban:
Jawaban di bawah garis ditulis pada tahun 2008.
C # 7 memperkenalkan pencocokan pola, yang sebagian besar telah menggantikan
as
operator, seperti sekarang Anda dapat menulis:Perhatikan bahwa
tt
masih dalam cakupan setelah ini, tetapi tidak ditetapkan secara pasti. (Hal ini jelas ditetapkan dalamif
tubuh.) Itu sedikit mengganggu dalam beberapa kasus, jadi jika Anda benar-benar peduli tentang memperkenalkan jumlah terkecil variabel mungkin dalam setiap lingkup, Anda mungkin masih ingin menggunakanis
diikuti oleh gips.Saya tidak berpikir satu pun dari jawaban sejauh ini (pada saat memulai jawaban ini!) Benar-benar menjelaskan di mana layak menggunakan yang mana.
Jangan lakukan ini:
Ini tidak hanya memeriksa dua kali, tetapi mungkin memeriksa hal-hal yang berbeda, jika
randomObject
merupakan bidang daripada variabel lokal. Dimungkinkan untuk "jika" untuk lulus tetapi kemudian para pemain gagal, jika utas lain mengubah nilai dirandomObject
antara keduanya.Jika
randomObject
benar - benar harus menjadi contohTargetType
, yaitu jika tidak, itu berarti ada bug, maka casting adalah solusi yang tepat. Itu melempar pengecualian segera, yang berarti bahwa tidak ada lagi pekerjaan yang dilakukan di bawah asumsi yang salah, dan pengecualian dengan benar menunjukkan jenis bug.Jika
randomObject
mungkin merupakan instance dariTargetType
danTargetType
merupakan tipe referensi, maka gunakan kode seperti ini:Jika
randomObject
mungkin merupakan instance dariTargetType
danTargetType
merupakan tipe nilai, maka kita tidak dapat menggunakannyaas
denganTargetType
sendirinya, tetapi kita dapat menggunakan tipe nullable:(Catatan: saat ini ini sebenarnya lebih lambat daripada + pemain . Saya pikir ini lebih elegan dan konsisten, tapi begitulah.)
Jika Anda benar-benar tidak membutuhkan nilai yang dikonversi, tetapi Anda hanya perlu tahu apakah ini merupakan instance dari TargetType, maka
is
operator adalah teman Anda. Dalam hal ini, tidak masalah apakah TargetType adalah tipe referensi atau tipe nilai.Mungkin ada kasus-kasus lain yang melibatkan obat generik di mana
is
berguna (karena Anda mungkin tidak tahu apakah T adalah tipe referensi atau tidak, jadi Anda tidak dapat menggunakannya sebagai) tetapi mereka relatif tidak jelas.Saya hampir pasti menggunakan
is
case type value sebelumnya, tidak pernah berpikir untuk menggunakan tipe nullable danas
bersama - sama :)EDIT: Perhatikan bahwa tidak ada yang di atas berbicara tentang kinerja, selain dari case type value, di mana saya telah mencatat bahwa unboxing ke tipe nilai nullable sebenarnya lebih lambat - tetapi konsisten.
Sesuai jawaban naasking, is-and-cast atau is-and-as sama cepat dan as-null-check dengan JIT modern, seperti yang ditunjukkan oleh kode di bawah ini:
Di laptop saya, semua ini dijalankan dalam waktu sekitar 60 ms. Dua hal yang perlu diperhatikan:
Jadi jangan khawatir tentang kinerjanya. Mari kita khawatirkan tentang kebenaran dan konsistensi.
Saya berpendapat bahwa is-and-cast (atau is-and-as) sama-sama tidak aman ketika berhadapan dengan variabel, karena jenis nilai yang dirujuknya dapat berubah karena ada utas lain antara pengujian dan para pemeran. Itu akan menjadi situasi yang sangat langka - tetapi saya lebih suka memiliki konvensi yang dapat saya gunakan secara konsisten.
Saya juga berpendapat bahwa as-then-null-check memberikan pemisahan keprihatinan yang lebih baik. Kami memiliki satu pernyataan yang mencoba konversi, dan kemudian satu pernyataan yang menggunakan hasilnya. Is-and-cast atau is-and-as melakukan tes dan kemudian upaya lain untuk mengkonversi nilai.
Dengan kata lain, adakah yang pernah menulis:
Itulah yang dilakukan dan dilakukan - meskipun jelas dengan cara yang lebih murah.
sumber
if (randomObject is TargetType convertedRandomObject){ // Do stuff with convertedRandomObject.Value}
atau menggunakanswitch
/case
melihat dokumen"as" akan mengembalikan NULL jika tidak memungkinkan untuk melakukan cast.
casting sebelumnya akan memunculkan eksepsi.
Untuk kinerja, meningkatkan pengecualian biasanya lebih mahal dalam waktu.
sumber
Inilah jawaban lain, dengan beberapa perbandingan IL. Pertimbangkan kelasnya:
Sekarang lihat IL yang dihasilkan setiap metode. Bahkan jika kode op tidak ada artinya bagi Anda, Anda dapat melihat satu perbedaan besar - isinst dipanggil diikuti oleh castclass dalam metode DirectCast. Jadi dua panggilan bukan satu pada dasarnya.
Kata kunci pertama versus castclass
Posting blog ini memiliki perbandingan yang layak antara dua cara melakukannya. Ringkasannya adalah:
Saya pribadi selalu menggunakan As, karena mudah dibaca dan direkomendasikan oleh tim pengembangan .NET (atau tetap saja Jeffrey Richter)
sumber
Salah satu perbedaan yang lebih kentara antara keduanya adalah bahwa kata kunci "sebagai" tidak dapat digunakan untuk casting ketika operator pemain terlibat:
Ini tidak akan dikompilasi (meskipun saya pikir itu dalam versi sebelumnya) pada baris terakhir karena kata kunci "sebagai" tidak memperhitungkan operator yang terlibat. Garis
string cast = (string)f;
bekerja dengan baik.sumber
sebagai tidak pernah melempar pengecualian jika ia tidak dapat melakukan konversi yang menghasilkan null sebagai gantinya ( seperti yang beroperasi pada tipe referensi saja). Jadi menggunakan seperti pada dasarnya setara dengan
Pemain C-style, di sisi lain, melemparkan pengecualian ketika tidak ada konversi yang dimungkinkan.
sumber
Tidak benar-benar jawaban untuk pertanyaan Anda, tetapi apa yang saya pikir merupakan poin terkait yang penting.
Jika Anda memprogram ke antarmuka, Anda tidak perlu melakukan cast. Semoga gips ini sangat langka. Jika tidak, Anda mungkin perlu memikirkan kembali beberapa antarmuka Anda.
sumber
Harap abaikan saran Jon Skeet, ulang: hindari pola uji-dan-cetak, yaitu .:
Gagasan bahwa ini lebih mahal dari gips dan tes nol adalah MITOS :
Ini adalah mikro-optimasi yang tidak berfungsi. Saya menjalankan beberapa tes nyata , dan test-and-cast sebenarnya lebih cepat daripada cast-and-null-comparison, dan lebih aman juga karena Anda tidak memiliki kemungkinan memiliki referensi nol dalam lingkup di luar jika seharusnya para pemain gagal.
Jika Anda menginginkan alasan mengapa uji coba lebih cepat, atau setidaknya tidak lebih lambat, ada alasan sederhana dan kompleks.
Sederhana: bahkan kompiler yang naif akan menyatukan dua operasi yang sama, seperti test-and-cast, menjadi satu test dan branch. cast-and-null-test dapat memaksa dua tes dan satu cabang, satu untuk tes tipe dan konversi menjadi nol pada kegagalan, satu untuk pemeriksaan nol itu sendiri. Paling tidak, mereka berdua akan mengoptimalkan untuk satu tes dan cabang, sehingga uji-dan-gips tidak akan lebih lambat atau lebih cepat dari uji-dan-nol-uji.
Kompleks: mengapa uji-dan tuang lebih cepat: uji tuang-dan-nol memasukkan variabel lain ke dalam lingkup luar yang harus dilacak oleh kompiler untuk liveness, dan mungkin tidak dapat mengoptimalkan variabel itu tergantung pada seberapa rumit kontrol Anda- mengalir. Sebaliknya, test-and-cast memperkenalkan variabel baru hanya dalam lingkup terbatas sehingga kompiler tahu bahwa variabel mati setelah lingkup keluar, dan dengan demikian dapat mengoptimalkan alokasi register dengan lebih baik.
Jadi tolong, HARAP biarkan ini "cast-and-null-test lebih baik daripada saran test-and-cast" DIE. SILAHKAN. test-and-cast lebih aman dan lebih cepat.
sumber
ref
parameter. Aman untuk variabel lokal, tetapi tidak untuk bidang. Saya akan tertarik untuk menjalankan benchmark Anda, tetapi kode yang Anda berikan di posting blog Anda tidak lengkap. Saya setuju dengan tidak mengoptimalkan mikro, tetapi saya tidak berpikir menggunakan nilai dua kali lebih mudah dibaca atau elegan daripada menggunakan "sebagai" dan tes nullity. (Saya pasti akan menggunakan gips lurus alih-alih "sebagai" setelah, adalah.)Jika para pemain gagal, kata kunci 'as' tidak memberikan pengecualian; itu menetapkan variabel ke null (atau ke nilai default untuk tipe nilai) sebagai gantinya.
sumber
Ini bukan jawaban untuk pertanyaan tetapi komentar untuk contoh kode pertanyaan:
Biasanya Anda tidak harus melemparkan Obyek dari mis. IMyInterface ke MyClass. Yang hebat tentang antarmuka adalah bahwa jika Anda mengambil objek sebagai input yang mengimplementasikan antarmuka, maka Anda tidak perlu peduli objek apa yang Anda peroleh.
Jika Anda menggunakan IMyInterface ke MyClass, maka Anda sudah menganggap bahwa Anda mendapatkan objek bertipe MyClass dan tidak masuk akal untuk menggunakan IMyInterface, karena jika Anda memberi makan kode Anda dengan kelas lain yang mengimplementasikan IMyInterface, itu akan merusak kode Anda ...
Sekarang, saran saya: jika antarmuka Anda dirancang dengan baik, Anda dapat menghindari banyak typecasting.
sumber
The
as
Operator hanya dapat digunakan pada jenis referensi, tidak dapat kelebihan beban, dan akan kembalinull
jika operasi gagal. Tidak akan pernah ada pengecualian.Casting dapat digunakan pada semua jenis yang kompatibel, dapat kelebihan beban, dan itu akan menimbulkan pengecualian jika operasi gagal.
Pilihan yang akan digunakan tergantung pada keadaan. Terutama, ini masalah apakah Anda ingin melemparkan pengecualian pada konversi yang gagal.
sumber
Jawaban saya hanya tentang kecepatan jika kami tidak memeriksa jenis dan kami tidak memeriksa nol setelah casting. Saya menambahkan dua tes tambahan ke kode Jon Skeet:
Hasil:
Jangan mencoba fokus pada kecepatan (seperti yang saya lakukan) karena semua ini sangat cepat.
sumber
as
konversi (tanpa pengecekan kesalahan) berjalan sekitar 1-3% lebih cepat daripada casting (sekitar 540ms versus 550ms pada 100 juta iterasi). Tidak akan membuat atau merusak aplikasi Anda.Selain semua yang sudah diungkapkan di sini, saya hanya menemukan perbedaan praktis yang menurut saya perlu diperhatikan, antara casting eksplisit
dibandingkan menggunakan
as
operator.Berikut ini contohnya:
Intinya: GenericCaster2 tidak akan berfungsi dengan tipe struct. GenericCaster akan.
sumber
Jika Anda menggunakan Office PIA yang menargetkan .NET Framework 4.X Anda harus menggunakan kata kunci as , jika tidak, ia tidak akan dikompilasi.
Casting tidak apa-apa saat menargetkan .NET 2.0:
Saat menargetkan .NET 4.X kesalahannya adalah:
kesalahan CS0656: Kompilator yang hilang diperlukan anggota 'Microsoft.CSharp.RuntimeBinder.Binder.Convert'
kesalahan CS0656: Kompilator yang hilang diperlukan anggota 'Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create'
sumber
Kata
as
kunci bekerja sama dengan cast eksplisit antara tipe referensi yang kompatibel dengan perbedaan utama yang tidak menimbulkan pengecualian jika konversi gagal. Sebaliknya, ini menghasilkan nilai nol dalam variabel target. Karena Pengecualian sangat mahal dalam hal kinerja, itu dianggap sebagai metode casting yang jauh lebih baik.sumber
Apa yang Anda pilih sangat tergantung pada apa yang diperlukan. Saya lebih suka casting eksplisit
karena jika objek harus dengan tipe IMyInterface dan tidak - itu pasti masalah. Lebih baik untuk mendapatkan kesalahan sedini mungkin karena kesalahan yang tepat akan diperbaiki daripada memperbaiki efek sampingnya.
Tetapi jika Anda berurusan dengan metode yang menerima
object
sebagai parameter maka Anda perlu memeriksa jenisnya yang tepat sebelum mengeksekusi kode apa pun. Dalam hal demikianas
akan bermanfaat agar Anda bisa menghindarInvalidCastException
.sumber
Tergantung, apakah Anda ingin memeriksa nol setelah menggunakan "sebagai" atau apakah Anda lebih suka aplikasi Anda untuk melemparkan pengecualian?
Aturan praktis saya adalah jika saya selalu berharap variabel dari tipe yang saya harapkan pada saat saya ingin saya menggunakan gips. Jika ada kemungkinan variabel tidak akan melakukan apa yang saya inginkan dan saya siap menangani nulls dari menggunakan as, saya akan menggunakan as.
sumber
Lihatlah tautan-tautan ini:
mereka menunjukkan kepada Anda beberapa detail dan tes kinerja.
sumber
Masalah OP terbatas pada situasi casting tertentu. Judul ini mencakup lebih banyak situasi.
Inilah gambaran umum dari semua situasi casting yang relevan yang saat ini dapat saya pikirkan:
sumber