'eşittir' ile boş kontrol ile deneyin


107

Resharper'ın bunu çevirmemi önerdiğini fark ettim:

if (myObj.myProp is MyType)
{
   ...
}

bunun içine:

var myObjRef = myObj.myProp as MyType;
if (myObjRef != null)
{
   ...
}

Neden bu değişikliği öneriyor? Resharper'ın optimizasyon değişiklikleri ve kod azaltma değişiklikleri önermesine alışkınım, ancak bu benim tek ifademi alıp iki satıra dönüştürmek istiyor gibi geliyor.

MSDN'ye göre :

Bir is ifadesi , aşağıdaki koşulların her ikisi de karşılanırsa doğru olarak değerlendirilir:

ifade boş değil. sentezleme için dökülebilir tip . Yani, formun cast ifadesi (type)(expression)bir istisna olmaksızın tamamlanacaktır.

isBoş kontrol için açıkça başka bir yerel değişken oluşturmaya gerek kalmadan tek bir satırda yanlış yorumluyor muyum veya aynı kontrolleri yapmıyor muyum?


1
myObjRef'i daha sonra kodda mı kullanıyorsunuz? Eğer öyleyseniz, MyPropbu değişiklikten sonra alıcıya ihtiyacınız olmayacak .
Varsayılan

Yanıtlar:


147

Çünkü sadece bir oyuncu kadrosu var. Şunu karşılaştırın:

if (myObj.myProp is MyType) // cast #1
{
    var myObjRef = (MyType)myObj.myProp; // needs to be cast a second time
                                         // before using it as a MyType
    ...
}

buna:

var myObjRef = myObj.myProp as MyType; // only one cast
if (myObjRef != null)
{
    // myObjRef is already MyType and doesn't need to be cast again
    ...
}

C # 7.0, desen eşleştirmeyi kullanarak daha kompakt bir sözdizimini destekler :

if (myObj.myProp is MyType myObjRef)
{
    ...
}

3
kesinlikle. 'is' kullanmak temelde return gibi bir şey yapmaktır ((MyProp olarak myProp) == null)
Bambu

2
Değişikliklere gelince, bu oldukça küçük. Boş kontrol, ikinci tip kontrolle oldukça benzer olacaktır. asbirkaç nanosaniye daha hızlı olabilir, ancak bunun erken bir mikrooptimizasyon olduğunu düşünüyorum.
Servy

4
Ayrıca, orijinal sürümün iş parçacığı açısından güvenli olmadığını unutmayın. Değeri myObjveya alçı myProparasında değişebilen (başka bir iş parçacığı tarafından) isistenmeyen davranışa neden olabilir.
Jeff E

1
Ayrıca, as+ kullanmanın != null, tanımlanmışsa ( boş olsa bile ) geçersiz kılınan !=işlecini de çalıştıracağını ekleyebilirim . İken en vakaların bu olmayan bir konu (özellikle de gerektiği gibi uygulanması durumunda) 'dir, (kötü kod, performans) bazı aşırı durumlarda istenilen edilmeyebilir. ( yine de oldukça aşırı olmalı )MyTypemyObjRef
Chris Sinclair

1
@Chris: Doğru, kodun doğru çevirisi kullanılır object.ReferenceEquals(null, myObjRef).
Ben Voigt

10

En iyi seçenek, şu şekilde kalıp eşleştirmeyi kullanmaktır:

if (value is MyType casted){
    //Code with casted as MyType
    //value is still the same
}
//Note: casted can be used outside (after) the 'if' scope, too

Tam olarak bu, sorudaki ikinci parçadan daha iyi nasıl?
Victor Yarema

Sorunun ikinci parçası is öğesinin temel kullanımına atıfta bulunuyor (değişken bildirimi olmadan) ve bu durumda türü iki kez kontrol edeceksiniz (biri is ifadesinden, diğeri dökümden önce)
Francesco Cattoni

6

Kemerin altında gerçekte ne olduğu hakkında henüz bir bilgi yok. Şu örneğe bir göz atın:

object o = "test";
if (o is string)
{
    var x = (string) o;
}

Bu, aşağıdaki IL'ye çevrilir:

IL_0000:  nop         
IL_0001:  ldstr       "test"
IL_0006:  stloc.0     // o
IL_0007:  ldloc.0     // o
IL_0008:  isinst      System.String
IL_000D:  ldnull      
IL_000E:  cgt.un      
IL_0010:  stloc.1     
IL_0011:  ldloc.1     
IL_0012:  brfalse.s   IL_001D
IL_0014:  nop         
IL_0015:  ldloc.0     // o
IL_0016:  castclass   System.String
IL_001B:  stloc.2     // x
IL_001C:  nop         
IL_001D:  ret   

Burada önemli olan isinstve castclassaramaları - her ikisi de nispeten pahalıdır. Bunu alternatifle karşılaştırırsanız, yalnızca bir isinstkontrol yaptığını görebilirsiniz :

object o = "test";
var oAsString = o as string;
if (oAsString != null)
{

}

IL_0000:  nop         
IL_0001:  ldstr       "test"
IL_0006:  stloc.0     // o
IL_0007:  ldloc.0     // o
IL_0008:  isinst      System.String
IL_000D:  stloc.1     // oAsString
IL_000E:  ldloc.1     // oAsString
IL_000F:  ldnull      
IL_0010:  cgt.un      
IL_0012:  stloc.2     
IL_0013:  ldloc.2     
IL_0014:  brfalse.s   IL_0018
IL_0016:  nop         
IL_0017:  nop         
IL_0018:  ret  

Ayrıca söz değer bir değer türü kullanacak olmasıdır unbox.anyyerine castclass:

object o = 5;
if (o is int)
{
    var x = (int)o;
}

IL_0000:  nop         
IL_0001:  ldc.i4.5    
IL_0002:  box         System.Int32
IL_0007:  stloc.0     // o
IL_0008:  ldloc.0     // o
IL_0009:  isinst      System.Int32
IL_000E:  ldnull      
IL_000F:  cgt.un      
IL_0011:  stloc.1     
IL_0012:  ldloc.1     
IL_0013:  brfalse.s   IL_001E
IL_0015:  nop         
IL_0016:  ldloc.0     // o
IL_0017:  unbox.any   System.Int32
IL_001C:  stloc.2     // x
IL_001D:  nop         
IL_001E:  ret   

Bununla birlikte, burada gördüğümüz gibi bunun daha hızlı bir sonuca dönüşmesi gerekmediğini unutmayın . Alçılar eskisi fakat kadar hızlı gerçekleştirilmelidir görünmektedir: gerçi bu soruyu istendi beri iyileştirmeler olmuştur Orada görünmektedir asve linqşimdi yaklaşık 3 kat daha hızlı.


4

Yeniden paylaşan uyarısı:

"Type check and direct cast can be replaced with try cast and check for null"

Her ikisi de çalışır, kodunuzun size nasıl daha çok uyduğuna bağlıdır. Benim durumumda bu uyarıyı görmezden geliyorum:

//1st way is n+1 times of casting
if (x is A) ((A)x).Run();
else if (x is B) ((B)x).Run();
else if (x is C) ((C)x).Run();
else if (x is D) ((D)x).Run();
//...
else if (x is N) ((N)x).Run();    
//...
else if (x is Z) ((Z)x).Run();

//2nd way is z times of casting
var a = x as Type A;
var b = x as Type B;
var c = x as Type C;
//..
var n = x as Type N;
//..
var z = x as Type Z;
if (a != null) a.Run();
elseif (b != null) b.Run();
elseif (c != null) c.Run();
...
elseif (n != null) n.Run();
...
elseif (x != null) x.Run();

Benim kodumda 2. yol daha uzun ve daha kötü performans.


1
Gerçek dünya örneğinizde, basitçe bir tasarım problemi var. Türleri kontrol ediyorsanız, sadece IRunable. Kontrol sende yoksa, belki kullanabilirsin dynamic?
M.Mimpen

3

Bana göre bu, o türden olup olmayacağı ihtimaline bağlı görünüyor. Nesne çoğu zaman bu türden ise, alçıyı önceden yapmak kesinlikle daha verimli olacaktır. Sadece ara sıra bu türdense, ilk olarak is ile kontrol etmek daha uygun olabilir.

Yerel bir değişken yaratmanın maliyeti, tür kontrolünün maliyeti ile karşılaştırıldığında çok azdır.

Okunabilirlik ve kapsam benim için tipik olarak en önemli faktörlerdir. ReSharper'a katılmıyorum ve "is" operatörünü yalnızca bu nedenle kullanıyorum; bu gerçek bir darboğazsa daha sonra optimize edin.

( myObj.myProp is MyTypeBu işlevde yalnızca bir kez kullandığınızı varsayıyorum )


0

Aynı zamanda ikinci bir değişiklik öneriyor olmalı:

(MyType)myObj.myProp

içine

myObjRef

Bu, orijinal koda kıyasla bir mülk erişimini ve bir dökümünü kaydeder. Ama değiştirdikten sonra sadece mümkün isiçin as.


@Default: Hayır değil. Bu, kodda olmadığı anlamına gelmez.
Ben Voigt

1
üzgünüm .. yanlış anlaşıldı. ancak, (MyType)cast başarısız olursa bir istisna atar. assadece döner null.
Varsayılan

@Default: Tür zaten kontrol edildiğinden is(söz konusu kod soruda) olduğundan cast başarısız olmayacak .
Ben Voigt

1
ancak, re # bu kodu değiştirmek istiyor - yani önerilen değişiklikten sonra orada olmayacak.
Varsayılan

Ben düşünüyorum (bana biraz zaman aldı) Ben burada düşünce takip ediyorum. İlk çizgi anlamına olduğu kodunda bir yerde ve bu hat ikinci satıra Re # öneri sonra basitleşeceğini?
Varsayılan

0

Bunun myObj.myProp'un myObjRef olan güçlü yazılmış bir sürümünü yapmak olduğunu söyleyebilirim. Bu daha sonra, bu değeri blokta referans alırken, bir atama yapmak zorunda kalırken kullanılmalıdır.

Örneğin, bu:

myObjRef.SomeProperty

bundan daha iyi:

((MyType)myObj.myProp).SomeProperty
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.