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:
if (randomObject is TargetType tt)
{
// Use tt here
}
Perhatikan bahwa tt
masih dalam cakupan setelah ini, tetapi tidak ditetapkan secara pasti. (Hal ini jelas ditetapkan dalam if
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 menggunakan is
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:
// Bad code - checks type twice for no reason
if (randomObject is TargetType)
{
TargetType foo = (TargetType) randomObject;
// Do something with foo
}
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 di randomObject
antara keduanya.
Jika randomObject
benar - benar harus menjadi contoh TargetType
, 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.
// This will throw an exception if randomObject is non-null and
// refers to an object of an incompatible type. The cast is
// the best code if that's the behaviour you want.
TargetType convertedRandomObject = (TargetType) randomObject;
Jika randomObject
mungkin merupakan instance dari TargetType
dan TargetType
merupakan tipe referensi, maka gunakan kode seperti ini:
TargetType convertedRandomObject = randomObject as TargetType;
if (convertedRandomObject != null)
{
// Do stuff with convertedRandomObject
}
Jika randomObject
mungkin merupakan instance dari TargetType
dan TargetType
merupakan tipe nilai, maka kita tidak dapat menggunakannya as
dengan TargetType
sendirinya, tetapi kita dapat menggunakan tipe nullable:
TargetType? convertedRandomObject = randomObject as TargetType?;
if (convertedRandomObject != null)
{
// Do stuff with convertedRandomObject.Value
}
(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 dan as
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:
using System;
using System.Diagnostics;
using System.Linq;
class Test
{
const int Size = 30000000;
static void Main()
{
object[] values = new object[Size];
for (int i = 0; i < Size - 2; i += 3)
{
values[i] = null;
values[i + 1] = "x";
values[i + 2] = new object();
}
FindLengthWithIsAndCast(values);
FindLengthWithIsAndAs(values);
FindLengthWithAsAndNullCheck(values);
}
static void FindLengthWithIsAndCast(object[] values)
{
Stopwatch sw = Stopwatch.StartNew();
int len = 0;
foreach (object o in values)
{
if (o is string)
{
string a = (string) o;
len += a.Length;
}
}
sw.Stop();
Console.WriteLine("Is and Cast: {0} : {1}", len,
(long)sw.ElapsedMilliseconds);
}
static void FindLengthWithIsAndAs(object[] values)
{
Stopwatch sw = Stopwatch.StartNew();
int len = 0;
foreach (object o in values)
{
if (o is string)
{
string a = o as string;
len += a.Length;
}
}
sw.Stop();
Console.WriteLine("Is and As: {0} : {1}", len,
(long)sw.ElapsedMilliseconds);
}
static void FindLengthWithAsAndNullCheck(object[] values)
{
Stopwatch sw = Stopwatch.StartNew();
int len = 0;
foreach (object o in values)
{
string a = o as string;
if (a != null)
{
len += a.Length;
}
}
sw.Stop();
Console.WriteLine("As and null check: {0} : {1}", len,
(long)sw.ElapsedMilliseconds);
}
}
Di laptop saya, semua ini dijalankan dalam waktu sekitar 60 ms. Dua hal yang perlu diperhatikan:
- Tidak ada perbedaan yang signifikan di antara mereka. (Bahkan, ada situasi di mana as-plus-nol-cek pasti adalah lebih lambat Kode di atas benar-benar membuat jenis pemeriksaan yang mudah karena untuk kelas disegel;. Jika Anda memeriksa sedang untuk antarmuka, tips keseimbangan sedikit mendukung as-plus-null-check.)
- Mereka semua sangat cepat. Ini tidak akan menjadi hambatan dalam kode Anda kecuali Anda benar-benar tidak akan melakukan apa pun dengan nilai-nilai sesudahnya.
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:
int value;
if (int.TryParse(text, out value))
{
value = int.Parse(text);
// Use value
}
Itulah yang dilakukan dan dilakukan - meskipun jelas dengan cara yang lebih murah.