it-swarm-id.com

Haruskah saya kembali dari fungsi lebih awal atau menggunakan pernyataan if?

Saya sering menulis fungsi semacam ini di kedua format, dan saya bertanya-tanya apakah satu format lebih disukai daripada yang lain, dan mengapa.

public void SomeFunction(bool someCondition)
{
    if (someCondition)
    {
        // Do Something
    }
}

atau

public void SomeFunction(bool someCondition)
{
    if (!someCondition)
        return;

    // Do Something
}

Saya biasanya kode dengan yang pertama karena itu adalah cara otak saya bekerja saat coding, meskipun saya pikir saya lebih suka yang ke-2 karena menangani penanganan kesalahan segera dan saya merasa lebih mudah untuk membaca

302
Rachel

Saya lebih suka gaya kedua. Dapatkan kasus yang tidak valid terlebih dahulu, baik dengan keluar atau menaikkan pengecualian yang sesuai, masukkan garis kosong di sana, lalu tambahkan tubuh "nyata" metode ini. Saya merasa lebih mudah dibaca.

400
Mason Wheeler

Jelas yang terakhir. Yang pertama tidak terlihat buruk sekarang, tetapi ketika Anda mendapatkan kode yang lebih kompleks, saya tidak bisa membayangkan ada orang yang berpikir seperti ini:

public int SomeFunction(bool cond1, string name, int value, AuthInfo perms)
{
    int retval = SUCCESS;
    if (someCondition)
    {
        if (name != null && name != "")
        {
            if (value != 0)
            {
                if (perms.allow(name)
                {
                    // Do Something
                }
                else
                {
                    reval = PERM_DENY;
                }
            }
            else
            {
                retval = BAD_VALUE;
            }
        }
        else
        {
            retval = BAD_NAME;
        }
    }
    else
    {
        retval = BAD_COND;
    }
    return retval;
}

lebih mudah dibaca daripada

public int SomeFunction(bool cond1, string name, int value, AuthInfo perms)
{
    if (!someCondition)
        return BAD_COND;

    if (name == null || name == "")
        return BAD_NAME;

    if (value == 0)
        return BAD_VALUE;

    if (!perms.allow(name))
        return PERM_DENY;

    // Do something
    return SUCCESS;
}

Saya sepenuhnya mengakui bahwa saya tidak pernah mengerti keuntungan dari titik keluar tunggal.

169
Jason Viers

Tergantung - Secara umum saya tidak akan keluar dari cara saya untuk mencoba dan memindahkan banyak kode untuk keluar dari fungsi lebih awal - kompiler biasanya akan menangani itu untuk saya. Walaupun begitu, jika ada beberapa parameter dasar di bagian atas yang saya butuhkan dan tidak dapat dilanjutkan, saya akan breakout lebih awal. Demikian juga, jika suatu kondisi menghasilkan blok if raksasa dalam fungsi, saya akan menerobosnya lebih awal sebagai akibatnya juga.

Meskipun demikian, jika suatu fungsi memerlukan beberapa data saat dipanggil, saya biasanya akan melempar pengecualian (lihat contoh) sebagai lawan dari hanya kembali.

public int myFunction(string parameterOne, string parameterTwo) {
  // Can't work without a value
  if (string.IsNullOrEmpty(parameterOne)) {
    throw new ArgumentNullException("parameterOne");
  } 
  if (string.IsNullOrEmpty(parameterTwo)) {
    throw new ArgumentNullException("parameterTwo");
  }

  // ...      
  // Do some work
  // ...

  return value;
}
32
rjzii

Saya lebih suka pengembalian awal.

Jika Anda memiliki satu titik masuk dan satu titik keluar maka Anda harus selalu melacak seluruh kode di kepala Anda sampai ke titik keluar (Anda tidak pernah tahu jika ada bagian kode lainnya di bawah melakukan sesuatu yang lain pada hasilnya, sehingga Anda harus melacaknya sampai ada). Anda melakukan itu tanpa masalah cabang mana yang menentukan hasil akhir. Ini sulit diikuti.

Dengan satu entri dan beberapa ada, Anda kembali ketika Anda memiliki hasil Anda dan tidak repot-repot melacak semuanya untuk melihat bahwa tidak ada yang melakukan hal lain untuk itu (karena tidak akan ada hal lain sejak Anda kembali). Ini seperti memiliki tubuh metode yang dipecah menjadi lebih banyak langkah, yang setiap langkah dengan kemungkinan untuk mengembalikan hasilnya atau membiarkan langkah berikutnya mencoba peruntungannya.

24
user7197

Dalam pemrograman C di mana Anda harus membersihkan secara manual ada banyak yang bisa dikatakan untuk pengembalian satu poin. Sekalipun saat ini tidak perlu untuk membersihkan sesuatu, seseorang mungkin mengedit fungsi Anda, mengalokasikan sesuatu dan perlu membersihkannya sebelum kembali. Jika itu terjadi, itu akan menjadi pekerjaan mimpi buruk melihat semua pernyataan kembali.

Dalam pemrograman C++ Anda memiliki destruktor dan bahkan sekarang pelindung ruang-keluar. Semua ini harus ada di sini untuk memastikan kode tersebut aman dari awal, sehingga kode dijaga dengan baik terhadap keluar awal dan oleh karena itu melakukannya tidak memiliki kelemahan logis dan murni masalah gaya.

Saya tidak cukup berpengetahuan tentang Java, apakah "akhirnya" kode blok akan dipanggil dan apakah finalizers dapat menangani situasi yang diperlukan untuk memastikan sesuatu terjadi.

C # Saya tentu tidak bisa menjawab.

D-bahasa memberi Anda penjaga ruang keluar yang tepat dan karena itu dipersiapkan dengan baik untuk keluar awal dan oleh karena itu tidak boleh menghadirkan masalah selain gaya.

Fungsi tentu saja tidak boleh terlalu lama di tempat pertama, dan jika Anda memiliki pernyataan beralih besar kode Anda mungkin juga sangat diperhitungkan.

13
CashCow

Saya menggunakan keduanya.

Jika DoSomething adalah 3-5 baris kode maka kode tersebut terlihat cantik menggunakan metode pemformatan pertama.

Tetapi jika memiliki lebih banyak baris dari itu, maka saya lebih suka format kedua. Saya tidak suka ketika kurung buka dan tutup tidak di layar yang sama.

9
azheglov

Pengembalian awal untuk-menang. Mereka bisa terlihat jelek, tetapi jauh lebih jelek daripada pembungkus if besar, terutama jika ada beberapa kondisi untuk diperiksa.

9
STW

Alasan klasik untuk single-entry-single-exit adalah bahwa jika semantik formal menjadi sangat buruk sebaliknya (alasan yang sama GOTO dianggap berbahaya).

Dengan kata lain, lebih mudah untuk mempertimbangkan kapan perangkat lunak Anda akan keluar dari rutin jika Anda hanya memiliki 1 pengembalian. Yang juga merupakan argumen terhadap pengecualian.

Biasanya saya meminimalkan pendekatan pengembalian awal.

8
Paul Nathan

Secara pribadi, saya lebih suka melakukan pemeriksaan kondisi lulus/gagal di awal. Itu memungkinkan saya untuk mengelompokkan sebagian besar kegagalan paling umum di bagian atas fungsi dengan sisa logika untuk diikuti.

7
nathan

Tergantung.

Pengembalian lebih awal jika ada beberapa kondisi jalan buntu yang jelas untuk segera diperiksa yang akan membuat menjalankan seluruh fungsi menjadi sia-sia. *

Tetapkan Retval + pengembalian tunggal jika fungsinya lebih kompleks dan bisa memiliki beberapa titik keluar jika tidak (masalah keterbacaan).

* Ini sering dapat menunjukkan masalah desain. Jika Anda menemukan bahwa banyak metode Anda perlu memeriksa beberapa keadaan eksternal/paramater atau semacamnya sebelum menjalankan sisa kode, itu mungkin sesuatu yang harus ditangani oleh pemanggil. .

6
Bobby Tables

Gunakan Jika

Dalam buku Don Knuth tentang GOTO, saya membacanya memberikan alasan untuk selalu memiliki kondisi yang paling mungkin didahulukan dalam pernyataan if. Di bawah asumsi bahwa ini masih merupakan ide yang masuk akal (dan bukan satu-satunya pertimbangan murni untuk kecepatan era). Saya akan mengatakan pengembalian awal bukan praktik pemrograman yang baik, terutama mengingat fakta bahwa mereka lebih sering daripada tidak digunakan untuk penanganan kesalahan, kecuali kode Anda lebih cenderung gagal daripada tidak gagal :-)

Jika Anda mengikuti saran di atas, Anda harus meletakkan pengembalian itu di bagian bawah fungsi, dan Anda mungkin bahkan tidak menyebutnya pengembalian di sana, cukup atur kode kesalahan dan kembalikan dua baris karenanya. Dengan demikian mencapai 1 entri 1 keluar ideal.

Khusus Delphi ...

Saya berpikir bahwa ini adalah praktik pemrograman yang baik untuk programmer Delphi, meskipun saya tidak punya bukti. Pra-D2009, kami tidak memiliki cara atom untuk mengembalikan nilai, kami memiliki exit; dan result := foo; atau kita bisa melempar pengecualian.

Jika Anda harus mengganti

if (true) {
 return foo;
} 

for

if true then 
begin
  result := foo; 
  exit; 
end;

anda mungkin bosan melihat bahwa di atas setiap fungsi Anda dan lebih suka

if false then 
begin
  result := bar;

   ... 
end
else
   result := foo;

dan hindari exit sama sekali.

3
Peter Turner

Saya setuju dengan pernyataan berikut:

Saya pribadi penggemar klausa penjaga (contoh kedua) karena mengurangi lekukan fungsi. Beberapa orang tidak menyukainya karena ini menghasilkan beberapa titik pengembalian dari fungsi, tetapi saya pikir itu lebih jelas dengan mereka.

Diambil dari pertanyaan ini di stackoverflow .

2
Toto

Saya lebih suka menulis:

if(someCondition)
{
    SomeFunction();
}
1
Josh K

Seperti Anda, saya biasanya menulis yang pertama, tetapi lebih suka yang terakhir. Jika saya memiliki banyak pemeriksaan bersarang saya biasanya refactor ke metode kedua.

Saya tidak suka bagaimana penanganan kesalahan dipindahkan dari cek.

if not error A
  if not error B
    if not error C
      // do something
    else handle error C
  else handle error B
else handle error A

Saya lebih memilih ini:

if error A
  handle error A; return
if error B
  handle error B; return
if error C
  handle error C; return

// do something
1
jolt

Saya menggunakan pengembalian awal hampir secara eksklusif hari ini, ke ekstrem. Saya menulis ini

self = [super init];

if (self != nil)
{
    // your code here
}

return self;

sebagai

self = [super init];
if (!self)
    return;

// your code here

return self;

tapi itu benar-benar tidak masalah. Jika Anda memiliki lebih dari satu atau dua tingkat sarang dalam fungsi Anda, mereka perlu rusak.

1
Dan Rosenstark

Kondisi di atas disebut "prasyarat". Dengan meletakkan if(!precond) return;, Anda secara visual mendaftar semua prasyarat.

Menggunakan blok "jika-lain" yang besar dapat meningkatkan indent overhead (saya lupa kutipan tentang indentasi 3-level).

0
Ming-Tang