Çizginin altındaki cevap 2008'de yazılmıştır.
C # 7 as
, şimdi yazabileceğiniz gibi , operatörün yerini büyük ölçüde alan desen eşleştirme özelliğini tanıttı :
if (randomObject is TargetType tt)
{
// Use tt here
}
Bundan tt
sonra hala kapsamda olduğunu , ancak kesinlikle atanmadığını unutmayın. (O olduğu kesinlikle içinde atanan if
vücuda.) Eğer gerçekten her kapsamda olası değişkenlerin en küçük numaranın faaliyete geçirilmesi önem böylece Yani, eğer bazı durumlarda biraz sinir bozucu, hala kullanmak isteyebilirsiniz is
alçıda izledi.
Şimdiye kadar cevapların hiçbirini düşünmüyorum (bu cevabı başlatırken!) Gerçekten hangisini kullanmaya değer olduğunu açıkladı.
Bunu yapma:
// Bad code - checks type twice for no reason
if (randomObject is TargetType)
{
TargetType foo = (TargetType) randomObject;
// Do something with foo
}
Bu sadece iki kez kontrol etmekle kalmaz, aynı randomObject
zamanda yerel bir değişken yerine bir alan ise farklı şeyleri kontrol ediyor olabilir . Başka bir iş parçacığı randomObject
ikisi arasındaki değeri değiştirirse, "if" in geçmesi ancak dökümün başarısız olması mümkündür .
Eğer randomObject
gerçekten gerektiği bir örneği olması TargetType
daha sonra döküm, araçlar bir hata var ki, değilse, yani doğru çözümdür. Bu hemen bir istisna atar, bu da yanlış varsayımlar altında daha fazla iş yapılmadığı anlamına gelir ve istisna hata türünü doğru bir şekilde gösterir.
// 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;
Eğer randomObject
olabilir bir örneği olması TargetType
ve TargetType
bir referans türüdür sonra böyle bir kodu kullanın:
TargetType convertedRandomObject = randomObject as TargetType;
if (convertedRandomObject != null)
{
// Do stuff with convertedRandomObject
}
Eğer randomObject
olabilir bir örneği olması TargetType
ve TargetType
bir değer türüdür, o zaman kullanamaz as
ile TargetType
kendisi, ama biz bir null türü kullanabilirsiniz:
TargetType? convertedRandomObject = randomObject as TargetType?;
if (convertedRandomObject != null)
{
// Do stuff with convertedRandomObject.Value
}
(Not: şu anda bu aslında + dökümden daha yavaş . Bence daha zarif ve tutarlı, ama işte başlıyoruz.)
Gerçekten dönüştürülen değeri gerekmez, ama sadece o olmadığını bilmek gerekiyorsa olan TargetType bir örneğini, daha sonra is
operatör senin arkadaşın. Bu durumda, TargetType'ın bir başvuru türü veya değer türü olması önemli değildir.
is
Faydalı olduğu durumlarda jenerikleri içeren başka durumlar da olabilir (çünkü T'nin bir referans türü olup olmadığını bilemeyebilirsiniz, bu nedenle kullanamazsınız) ancak nispeten belirsizdirler.
Neredeyse kesinlikle is
değer türü durum için kullandım, nullable türü ve as
birlikte kullanmayı düşünmeden :)
DÜZENLEME: Yukarıdaki özelliklerin hiçbirinin, null değeri olan bir değer türüne göre kutunun kaldırılmasının aslında daha yavaş olduğunu ancak tutarlı olduğunu belirten değer türü durumu dışında performanstan bahsetmediğini unutmayın.
Naasking'in cevabına göre, aşağıdaki kodda gösterildiği gibi, modern JIT'ler ile hem-ve-cast veya is-and-as kadar hızlıdır:
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);
}
}
Dizüstü bilgisayarımda, bunların hepsi yaklaşık 60ms. Dikkat edilmesi gereken iki şey:
- Aralarında önemli bir fark yok. (Aslında, as-plus-null-check'in kesinlikle daha yavaş olduğu durumlar vardır . Yukarıdaki kod aslında tip kontrolünü kolaylaştırır çünkü kapalı bir sınıf içindir; eğer bir arayüz kontrol ediyorsanız, denge biraz ipuçları as-plus-null-check lehine.)
- Hepsi delicesine hızlı. Bu sadece olmaz gerçekten yapmayacak sürece kodunuzu darboğaz olmak bir şey sonradan değerlerle.
Performans hakkında endişelenmeyelim. Doğruluk ve tutarlılık konusunda endişelenelim.
I-ve-cast (veya is-and-as) değişkenlerle uğraşırken hem güvensiz olduğunu, çünkü değerin türü test ve döküm arasındaki başka bir iş parçacığı nedeniyle değişebileceğinden emin olun. Bu oldukça nadir bir durum olurdu - ama tutarlı bir şekilde kullanabileceğim bir sözleşmeyi tercih ederim.
Ben de o zaman null olarak kontrol endişelerin daha iyi ayrılmasını sağlar. Bir dönüşümü deneyen bir ifademiz ve ardından sonucu kullanan bir ifademiz var. İs-and-cast veya is-and-as bir test gerçekleştirir ve ardından değeri dönüştürmek için başka bir girişimde bulunur.
Diğer bir ifadeyle, herkes olur hiç yazma:
int value;
if (int.TryParse(text, out value))
{
value = int.Parse(text);
// Use value
}
Açıkçası daha ucuz bir şekilde olmasına rağmen, bu tür ve döküm ne yapıyor?