İf / return'de en iyi yöntem


60

İfademi aldığımda neyin daha iyi dönüş yolu olarak kabul edildiğini bilmek istiyorum if.

Örnek 1:

public bool MyFunction()
{
   // Get some string for this example
   string myString = GetString();

   if (myString == null)
   {
      return false;
   }
   else
   {
      myString = "Name " + myString;
      // Do something more here...
      return true;
   }
}

Örnek 2:

public bool MyFunction()
{
   // Get some string for this example
   string myString = GetString();

   if (myString == null)
   {
      return false;
   }

   myString = "Name " + myString;
   // Do something more here...
   return true;
}

Her iki örnekte de görebileceğiniz gibi, işlev dönecektir, true/falseancak elseilk örnekte olduğu gibi ifadeyi koymak iyi bir fikir mi yoksa belirtmemek daha mı iyi?


7
Eğer sadece 'if' içerisindeki hataları kontrol ediyorsanız, 'else' i eklememelisiniz, çünkü hatalar gerçek mantığın bir parçası olarak düşünülmemelidir.
Mert Akcakaya,

2
Şahsen yan etkilere neden olan işlev konusunda daha çok endişeliydim, ama sanırım bu sadece kötü seçilmiş bir örnek mi?
jk.


14
Bana göre, ilk versiyon odadaki ödevlerini bitirmeyen tüm öğrencileri işten çıkarmak, ve bir kez gittikten sonra geri kalan öğrencilerin "Şimdi ödevini bitirdiysen ..." demesi gibi . Mantıklı ama gereksiz. Koşullu artık gerçekten bir şartlı olmadığı için, bunu düşürme eğilimindeyim else.
Nicole,

1
Yapmak istediğiniz 'Burada daha fazla bir şeyler yapın' nedir? Bu, işlevinizi tasarlamanızı tamamen değiştirebilir.
Benedict

Yanıtlar:


81

Örnek 2, koruyucu blok olarak bilinir. Bir şeyler ters gittiğinde (yanlış parametre ya da geçersiz durum) erken istisna geri döndürmek / atmak daha uygundur. Normal mantık akışında Örnek 1 kullanmak daha iyidir


+1 - iyi bir ayrım ve ne cevaplayacağım.
Telastyn

3
@ pR0P'ler aşağıda aynı cevaba sahiptir ve neden izlenmesi gereken daha temiz bir yol olduğu konusunda bir kod örneği sağlar. İyi cevap için +1.

Bu soru SO üzerinde defalarca soruldu ve tartışmalı olsa da bu iyi bir cevap. Sadece sondan geri dönmek için eğitilmiş birçok insanın bu konseptle ilgili bazı sorunları var.
Bill K

2
+1. Uygunsuz bloklardan kaçınılması, ok koduna neden olma eğilimindedir.
Brian

Her iki örnekte de koruma bloğu yok mu?
Ernie

44

Benim kişisel tarzı tek kullanmaktır ifbekçi blokları ve if/ elsefiili yöntem işleme kodunda.

Bu durumda, myString == nullkoruyucu koşulu olarak kullanıyorsunuz , bu yüzden tekli ifmodeli kullanma eğilimindeyim .

Biraz daha karmaşık bir kod düşünün:

Örnek 1:

public bool MyFunction(myString: string){

    //guard block
    if (myString == null){
        return false;
    }
    else{
        //processing block
        myString = escapedString(myString);

        if (myString == "foo"){
            //some processing here
            return false;
        }
        else{
            myString = "Name " + myString;
            //other stuff
            return true;
        }
    }
}

Örnek 2:

public bool MyFunction(myString: string){

    //guard block
    if (myString == null){
        return false;
    }

    //processing block
    myString = escapedString(myString);

    if (myString == "foo"){
        //some processing here
        return false;
    }
    else{
        myString = "Name " + myString;
        //other stuff
        return true;
    }
}

Örnek 1'de, hem koruma hem de yöntemin geri kalanı if/ elseformundadır. ifMetodu geri kalanı if/ elseformunu kullanırken koruyucu bloğun tek formda olduğu Örnek 2 ile karşılaştırın . Şahsen, örnek 2'nin dağınık ve fazla girintili göründüğü halde 2. örneği daha kolay anlaşılır buluyorum.

Bunun tartışmalı bir örnek olduğunu ve else ifonu temizlemek için ifadeler kullanabileceğinizi unutmayın , ancak koruyucu bloklar ve gerçek işlev işleme kodu arasındaki farkı göstermeyi amaçlıyorum.

İyi bir derleyici, her ikisi için de aynı çıktıyı üretmelidir. Birini veya diğerini kullanmanın tek nedeni kişisel tercih veya mevcut kodun tarzına uymaktır.


18
İkinci örnekten kurtulursanız, Örnek 2'nin okunması daha kolay olurdu.
briddums

18

Şahsen ikinci yöntemi tercih ederim. Sanki daha kısa, daha az girinti ve okunması daha kolay gibi hissediyorum.


Daha az girinti için +1. Birden fazla çek varsa bu
iğrenç

15

Benim kişisel pratiğim:

  • Çeşitli çıkış noktalarına sahip fonksiyonları sevmiyorum, bakımını ve takibini zor buldum, kod değişiklikleri bazen iç mantığını bozuyor çünkü doğası gereği biraz özensiz. Karmaşık bir hesaplama olduğunda, başlangıçta bir dönüş değeri oluşturur ve sonunda geri veririm. Bu beni her if-else, switch vb. Yolları dikkatlice takip etmeye zorlar, değeri doğru yerlere doğru şekilde ayarlar. Ayrıca varsayılan bir dönüş değeri ayarlamaya ya da başlangıçta başlatılmamış bırakmaya karar vermek için biraz zaman harcıyorum. Bu yöntem aynı zamanda mantık veya dönüş değeri tipi veya anlamı değiştiğinde yardımcı olur.

Örneğin:

public bool myFunction()
{
   // First parameter loading
   String myString = getString();

   // Location of "quick exits", see the second example
   // ...

   // declaration of external resources that MUST be released whatever happens
   // ...

   // the return variable (should think about giving it a default value or not) 
   // if you have no default value, declare it final! You will get compiler 
   // error when you try to set it multiple times or leave uninitialized!
   bool didSomething = false;

   try {
     if (myString != null)
     {
       myString = "Name " + myString;
       // Do something more here...

       didSomething = true;
     } else {
       // get other parameters and data
       if ( other conditions apply ) {
         // do something else
         didSomething = true;
       }
     }

     // Edit: previously forgot the most important advantage of this version
     // *** HOUSEKEEPING!!! ***

   } finally {

     // this is the common place to release all resources, reset all state variables

     // Yes, if you use try-finally, you will get here from any internal returns too.
     // As I said, it is only my taste that I like to have one, straightforward path 
     // leading here, and this works even if you don't use the try-finally version.

   }

   return didSomething;
}
  • Bunun tek istisnası: "hızlı çıkış" başlangıçta (ya da nadir durumlarda, işlemin içinde). Eğer gerçek hesaplama mantığı giriş parametrelerinin ve dahili durumların belirli bir kombinasyonunu kaldıramazsa veya algoritmayı çalıştırmadan kolay bir çözüme sahipse, tüm kodun bloklar halinde (bazen derin) içine alınmasına yardımcı olmaz. Bu çekirdek mantığın bir parçası değil, "istisnai bir durum" dür, bu yüzden tespit ettiğimde hesaplamadan çıkmalıyım. Bu durumda başka bir dal yoktur, normal şartlarda yürütme basitçe devam eder. (Tabii ki, "istisnai devlet" bir istisna fırlatılarak daha iyi ifade edilir, ancak bazen bir fazlalıktır.)

Örneğin:

public bool myFunction()
{
   String myString = getString();

   if (null == myString)
   {
     // there is nothing to do if myString is null
     return false;
   } 

   myString = "Name " + myString;
   // Do something more here...

   // not using return value variable now, because the operation is straightforward.
   // if the operation is complex, use the variable as the previous example.

   return true;
}

"Bir çıkış" kuralı ayrıca hesaplama serbest bırakmanız gereken harici kaynaklar gerektirdiğinde veya işlevden çıkmadan önce sıfırlamanız gerektiğini belirtir; bazen daha sonra geliştirme sırasında eklenirler. Algoritmanın içindeki çoklu çıkışlarda, tüm dalları düzgün bir şekilde genişletmek çok daha zordur. (İstisnalar meydana gelebilirse, nadir istisnai durumlarda yan etkilerden kaçınmak için, serbest bırakma / sıfırlama en sonunda bir bloğa konmalıdır ...).

Durumunuz "gerçek işten önce hızlı çıkış" kategorisine giriyor gibi görünüyor ve ben de senin Örnek 2 versiyonun gibi yazarım.


9
+1 "Çeşitli çıkış noktalarına sahip işlevleri sevmiyorum"
Corv1nus

@ LorandKedves - bir örnek ekledi - umursamıyorum umarım.
Matthew Flynn

@MatthewFlynn peki, cevabımın sonunda önerdiklerimin tersini umursamıyorsanız ;-), örneğe devam ediyorum, umarım sonunda ikimiz için de iyi olur :-)
Lorand Kedves

@MatthewFlynn (üzgünüm, sadece bir perişan piçim ve görüşümü netleştirmeme yardım ettiğin için teşekkür ederim. Umarım şu anki sürümü
beğenirsin

13
Çoklu çıkış noktalarına sahip fonksiyonlardan ve değişken sonuç değişkenine sahip fonksiyonlardan ilki, bana göre iki kötülüğün daha az olduğu görünüyor.
Jon Purdy

9

İki nedenden ötürü bulabildiğim yerde güvenlik blokları kullanmayı tercih ediyorum:

  1. Bazı özel koşullar göz önüne alındığında hızlı bir çıkış sağlarlar.
  2. Daha sonra kodda ifadeler varsa karmaşık ve gerekli olmayan gerekliliği kaldırın.

Genel olarak konuşursak, yöntemin temel işlevselliğinin net ve minimal olduğu yöntemleri görmeyi tercih ederim. Muhafaza blokları görsel olarak bunun gerçekleşmesine yardımcı olur.


5

"Düşüşte" yaklaşımını seviyorum:

public bool MyFunction()
{
   string myString = GetString();

   if (myString != null)
   {
     myString = "Name " + myString;
     return true;
    }
    return false;
}

Eylemin belirli bir koşulu var, başka herhangi bir şey yalnızca varsayılan "geçme" dönüşüdür.


1

Tek bir if koşulum varsa, stil hakkında çok fazla zaman harcamak istemem. Fakat çoklu koruma koşullarım varsa style2'yi tercih ederim

Bunu hayal et. Testlerin karmaşık olduğunu ve karmaşıklığı önlemek için gerçekten tek bir if-ORed durumuna bağlamak istemediğinizi varsayalım:

//Style1
if (this1 != Right)
{ 
    return;
}
else if(this2 != right2)
{
    return;
}
else if(this3 != right2)
{
    return;
}
else
{
    //everything is right
    //do something
    return;
}

e karşı

//Style 2
if (this1 != Right)
{ 
   return;
}
if(this2 != right2)
{
    return;
}
if(this3 != right2)
{
    return;
}


//everything is right
//do something
return;

Burada iki ana avantaj var.

  1. Kodu tek bir fonksiyonda görsel olarak iki logcal bloğa ayırıyorsunuz: bir üst doğrulama bloğu (koruma koşulları) ve daha düşük çalıştırılabilir kod bloğu.

  2. Bir koşul eklemek / kaldırmak zorunda kalırsanız, if-elseif-else merdiveninin tamamını susturma şansınızı azaltırsınız.

Bir diğer küçük avantaj da, ilgilenilmesi gereken daha az sayıda diş teli kullanmanızdır.


0

Bu daha iyi sesler meselesi olmalı.

If condition
  do something
else
  do somethingelse

o zaman kendini daha iyi ifade eder

if condition
  do something
do somethingelse

küçük metotlar için fark olmaz, fakat daha büyük bileşik metotları için ayrı olarak ayrılmayacağından anlaşılmasını zorlaştırabilir


3
Bir yöntem çok uzunsa, ikinci formun anlaşılması zorsa, çok uzun.
kevin cline

7
İki vakanız sadece do somethingbir iade içeriyorsa eşdeğerdir . Aksi halde, ikinci kod , ilk bloğun nasıl çalıştığını do somethingelsebelirten ififadeden bağımsız olarak çalışır.
Adnan

Bunlar ayrıca OP'nin örneklerinde açıkladığı şeydir. Sorunun çizgisini takip ederken
José Valente

0

Örnek 1'i tahriş edici buluyorum, çünkü değer döndürme işlevinin sonundaki eksik geri dönüş ifadesi derhal "Bekle, yanlış bir şey var" -flag'i tetikler. Böylece böyle durumlarda, örnek 2 ile giderdim.

Bununla birlikte, genellikle işlevin amacına bağlı olarak, örneğin hata işleme, günlüğe kaydetme vb. Gibi daha fazla rol vardır. Bu yüzden Lorand Kedves'in bu konudaki cevabını bekliyorum ve genellikle maliyetinde bile bir çıkış noktasına sahibim. ek bir bayrak değişkeni. Çoğu durumda bakım ve sonraki uzantıları kolaylaştırır.


0

Senin ne zaman ififadesi her zaman döndürür sonra işlevinde kalan kodu için bir başka kullanmak için hiçbir neden yoktur. Bunu yapmak, fazladan satır ve fazladan girinti ekler. Gereksiz kod eklemek, okumayı zorlaştırır ve herkesin bildiği gibi Okuma kodu Zor .


0

Örnekte else, elbette gereksiz, ANCAK ...

Kod satırları arasında hızlı bir şekilde kayma yaparken, birinin gözleri tipik olarak kod akışına dair bir fikir edinmek için parantezlere ve girintilere bakar; onlar aslında kodun kendisine karar vermeden önce. Bu nedenle, elseikinci kod bloğu etrafına parantez ve girinti yazmak, okuyucunun bunun bir blok veya diğerinin çalışacağı "A veya B" durumu olduğunu görmesini daha hızlı hale getirir. Diğer bir deyişle, okuyucu returnbaşlangıçtan sonra görmeden ÖNCE diş tellerini ve girintileri görür if.

Kanımca, fazladan bir şey eklemenin aslında onu DAHA FAZLA okunabilir kıldığı, nadir olmayan durumlardan biri olduğunu düşünüyorum.


0

Örnek 2'yi tercih ederim çünkü fonksiyonun bir şey döndürdüğü hemen belli oluyor . Fakat bundan daha fazlası, böyle bir yerden dönmeyi tercih ederim:

public bool MyFunction()
{
    bool result = false;

    string myString = GetString();

    if (myString != nil) {
        myString = "Name " + myString;

        result = true;
    }

    return result;
}

Bu stil ile yapabilirim:

  1. Hemen bir şey iade ettiğime bakın.

  2. Fonksiyonun her başlatılmasının sonucunu sadece bir kesme noktası ile yakalayın.


Bu, soruna nasıl yaklaşacağımla aynı. Tek fark, myString değerlendirmesini yakalamak ve return değeri olarak kullanmak için sonuç değişkenini kullanmamdır.
Chuck Conway

@ChuckConway Anlaştık, ancak OP'nin prototipine bağlı kalmaya çalışıyordum.
Caleb

0

Kişisel tarzım gitme eğilimindedir

function doQuery(string) {
    if (!query(string)) {
        query("ABORT");
        return false;
    } // else
    if(!anotherquery(string) {
        query("ABORT");
        return false;
    } // else
    return true;
}

Yorum elseakışını program akışını göstermek ve okunaklı tutmak için kullanmak, ancak çok fazla adım atılırsa ekranda kolayca erişebilecek büyük girintilerden kaçınmak.


1
Şahsen, umarım asla senin kodunla çalışmak zorunda kalmam.
12'de bir CVn

Birden fazla çekiniz varsa bu yöntem biraz uzayabilir. Her bir fıkra döndürüldüğünde, kodun bölümlerini atlamak için goto deyimini kullanmaya benzer.
Chuck Conway

Bu ve seçim elsecümleleri dahil olmak üzere kod arasında bir seçim olsaydı, ikincisini seçerdim, çünkü derleyici de onları görmezden gelir. O bağlı olacağını rağmen else if değil bir kere orijinal fazla girinti artan - bu eğer yaptığımız senle aynı olurdu. Ama benim seçimim, elseeğer bu stile izin verilirse , tamamen bırakmak olacaktır .
Mark Hurd

0

Yazardım

public bool SetUserId(int id)
{
   // Get user name from id
   string userName = GetNameById(id);

   if (userName != null)
   {
       // update local ID and userName
       _id = id;
       _userNameLabelText = "Name: " + userName;
   }

   return userName != null;
}

Bu tarzı tercih ediyorum çünkü geri döneceğim çok açık userName != null.


1
Asıl soru, neden bu tarzı tercih ediyorsun?
Caleb,

... Açıkçası, bu durumda ipi neden istediğinizi bile bilmiyorum, sonunda neden bir ihtiyaç duymadığınızı gerçekten fazladan bir boole testi eklediğinizi söylemiyorsunuz.
Shadur

@Shadur Ekstra boole testi ve kullanılmayan myStringsonuçların farkındayım . Ben sadece fonksiyonu OP'den kopyaladım. Daha gerçek görünen bir şeyle değiştireceğim. Boolean testi önemsizdir ve sorun olmamalıdır.
tia

1
@Shadur Burada optimizasyon yapmıyoruz, değil mi? Amacım kodumun okunmasını ve sürdürülmesini kolaylaştırmak ve şahsen bunu bir referansın bedeli ile ödeyeceğim.
tia

1
@tia Kodunuzun ruhunu seviyorum. UserName değerini iki kez değerlendirmek yerine, ilk değerlendirmeyi bir değişkende yakalar ve bunu geri döndürürüm.
Chuck Conway

-1

Başlıca kuralım ya başka bir şey olmadan if-return yapmak (çoğunlukla hatalar için) ya da tüm olasılıklar üzerinde tam bir if-if-if zinciri yapmaktır.

Bu yolla dallanmayla çok fazla süslü olmaya çalışmaktan kaçınıyorum, çünkü ne zaman bitirdim ki uygulama mantığımı if'lerimde kodluyorum, sürdürmeleri zorlaşıyor. ! <<> HATA, bir dahaki sefere enum'a üçüncü bir seçenek eklemek eşek için bir acı haline gelir.

Sizin durumunuzda, örneğin, eğer "null ise return" ortak bir kalıp olduğundan emin olduğundan ve üçüncü bir olasılık için endişelenmeniz gerekmeyeceğinden (null / null değil) endişelenmeniz gerekmeyeceğinden bir ova kullanacağım. gelecekte.

Bununla birlikte, test veriler üzerinde daha ayrıntılı bir inceleme yapan bir şey olsaydı, bunun yerine tam bir if-başka bir hata yapardım.


-1

Yolda istenmeyen kodlara yol açabileceğini, Örnek 2 için birçok tuzaklar olduğunu düşünüyorum. Buradaki ilk odak noktası 'myString' değişkenini çevreleyen mantığa dayanıyor. Bu nedenle açık olmak gerekirse, tüm koşulların sınanması bilinen ve varsayılan / bilinmeyen mantık için bir kod bloğunda yapılmalıdır .

Ya daha sonra kod istemeden yanlışlıkla çıktıyı değiştiren örnek 2'ye tanıtıldıysa:

   if (myString == null)
   {
      return false;
   }

   //add some v1 update code here...
   myString = "And the winner is: ";
   //add some v2 update code here...
   //buried in v2 updates the following line was added
   myString = null;
   //add some v3 update code here...
   //Well technically this should not be hit because myString = null
   //but we already passed that logic
   myString = "Name " + myString;
   // Do something more here...
   return true;

elseNull kontrolünün hemen ardından gelen bloğun gelecekteki sürümlere geliştirmeleri ekleyen programcıları tüm mantığı bir araya getirdiğini düşünüyorum çünkü şimdi orijinal kural için istenmeyen bir mantık dizesi var (eğer değer ise boş).

Bu konudaki inancımı, aşağıdakileri ifade eden Codeplex'teki C # kılavuzlarının bazılarına (buradaki bağlantıya: http://csharpguidelines.codeplex.com/ ) borç verdim :

"Varsayılan bloğun (başka) boş olması gerekiyorsa açıklayıcı bir yorum ekleyin. Ayrıca, bu bloğa ulaşılmaması gerekiyorsa, mevcut durumlardan kaynaklanabilecek gelecekteki değişiklikleri tespit etmek için bir InvalidOperationException atın. Bu, daha iyi bir kod sağlar, çünkü "Kodun gidebileceği tüm yollar düşünülmüş."

Bunun gibi mantık blokları kullanırken, her zaman açıkça tüm kod yollarını açık bir şekilde hesaba katan ve kodu istenmeyen mantık sonuçlarına açık bırakmamak için varsayılan bir bloğun eklenmiş olması iyi bir programlama uygulamasıdır.


Örnek ikide bir başkasına sahip olmak, bütün durumları dikkate almaz mı? Amaçlanan sonuç İD'in devam etmesidir. Bu durumda başka bir madde eklemek, sadece yöntemin karmaşıklığını arttırır.
Chuck Conway

Söz konusu bütün mantıkları özlü ve deterministik bir blokta barındırma yeteneğine dayanarak aynı fikirde değilim. Odak myStringdeğeri manipüle etmekte ve ifbloktan sonraki kod ! = Null ise sonuçtaki mantıktır. Bu nedenle odak, bu belirli değeri manipüle eden ilave mantık üzerine olduğundan , bir elseblokta kapsüllenmesi gerektiğini düşünüyorum . Aksi takdirde istemeden mantığı ayırdığımı ve istenmeyen sonuçları ortaya çıkardığımı gösterdiğim için bu potansiyel yaratıyor. Her zaman olmayabilir, ama bu gerçekten en iyi pratik fikir tarzı bir soruydu.
saat
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.