Mengapa memiliki metode yang mengembalikan bool / int dan memiliki objek aktual sebagai parameter output?

12

Saya melihat pola kode berikut di semua tempat di basis kode perusahaan saya (aplikasi .NET 3.5):

bool Foo(int barID, out Baz bazObject) { 
    try { 
            // do stuff
            bazObject = someResponseObject;

            return true;
    }
    catch (Exception ex) { 
        // log error
        return false;
    }
}

// calling code
BazObject baz = new BazObject();
fooObject.Foo(barID, out baz);

if (baz != null) { 
    // do stuff with baz
}

Saya mencoba membungkus kepala saya di sekitar mengapa Anda akan melakukan ini daripada memiliki Foometode hanya mengambil ID dan mengembalikan Bazobjek, bukannya mengembalikan nilai yang tidak digunakan dan memiliki objek yang sebenarnya menjadi parameter ref atau output.

Apakah ada beberapa manfaat tersembunyi dari gaya pengkodean ini yang saya lewatkan?

Wayne Molina
sumber
Dalam contoh Anda, bazmenjadi nulldan kembali boolmakhluk falseyang tidak setara. new BazObject()tidak pernah null, jadi kecuali bazObjectdiperbarui sebelum Exceptiondilemparkan Foo, ketika falsedikembalikan baztidak akan pernah null. Ini akan membantu sangat jika spesifikasi untuk Fooyang tersedia. Sebenarnya, ini mungkin masalah paling serius yang ditunjukkan oleh kode ini.
Steve Powell
Ingatan saya kabur karena ini sudah lama sekali, tetapi saya pikir saya telah mengacaukan contoh dan memeriksa apakah "baz" salah, bukan jika itu nol. Bagaimanapun, polanya tampak sangat kuno bagi saya, seperti berasal dari VB6 dan pengembang tidak pernah repot-repot memperbaiki kodenya (yang tidak dia lakukan)
Wayne Molina

Jawaban:

11

Anda biasanya menggunakan pola itu sehingga Anda dapat menulis kode seperti ini:

if (Foo(barId, out bazObject))
{
  //DoStuff with bazobject
}

misalnya digunakan di CLR untuk TryGetValue di kelas kamus misalnya. Itu menghindari beberapa redundansi tetapi parameter out dan ref selalu tampak agak berantakan bagi saya

Homde
sumber
1
+1, inilah alasan mengapa, meskipun saya cenderung mengembalikan objek dengan boolean dan objek daripada mengembalikan keduanya secara terpisah
pdr
Akan menandai ini sebagai jawaban karena memang menjelaskan alasannya, meskipun konsensus tampaknya sudah cukup usang kecuali dalam keadaan tertentu.
Wayne Molina
Jawaban ini membenarkan penggunaan pola ini. Tetapi jika itu SEMUA ATAS basis kode Anda, itu mungkin praktik yang buruk seperti sisi Sean.
Codism
11

Ini adalah kode gaya C lama, sebelum ada pengecualian. Nilai kembali menunjukkan apakah metode itu berhasil atau tidak, dan jika itu, parameter akan diisi dengan hasilnya.

Di .net kami memiliki pengecualian untuk tujuan itu. Seharusnya tidak ada alasan untuk mengikuti pola ini.

[Sunting] Jelas ada implikasi kinerja dalam penanganan pengecualian. Mungkin itu ada hubungannya dengan itu. Namun, dalam cuplikan kode itu sudah ada pengecualian. Akan lebih bersih untuk membiarkannya naik tumpukan sampai tertangkap di tempat yang lebih tepat.

Sean Edwards
sumber
Tidak. Tidak setuju dengan itu. Jangan pernah gunakan pengecualian untuk kondisi yang diharapkan. Jika Anda mengurai string ke int, misalnya, selalu mungkin seseorang akan melewatkan string yang tidak dapat diurai. Anda seharusnya tidak melempar pengecualian dalam kasus itu
pdr
Bukan itu yang saya katakan. Jika Anda membaca kode yang diposting OP, ada kasus pengecualian yang terjadi secara eksplisit, tetapi metode menangkapnya dan mengembalikan false. Pertanyaan ini terkait dengan penanganan kesalahan, bukan kondisi yang diharapkan.
Sean Edwards
Ya, itu tampaknya terutama digunakan dalam metode yang memuat data dari database misalnya, if (baz.Select())tetapi lebih sering nilai pengembaliannya dibuang begitu saja dan nilainya diperiksa terhadap null atau properti tertentu.
Wayne Molina
@Sean, lihat apa yang Anda katakan, saya hanya keberatan dengan frasa "Dalam. NET kami memiliki pengecualian untuk tujuan itu." Pengecualian melayani tujuan yang sama sekali berbeda bagi saya
pdr
1
Ok, biarkan saya jelas. Memang benar bahwa Anda tidak boleh hanya menambahkan boolean ke setiap metode ke akun untuk kesalahan pengkodean pada argumen yang tidak valid. Tetapi di mana input ke metode berasal dari pengguna daripada pengembang Anda harus dapat mencoba menangani input tanpa melemparkan pengecualian jika mereka salah.
pdr
2

Diberikan potongan kode itu terlihat benar-benar tidak berguna. Bagi saya pola kode awal akan menyarankan bahwa null untuk BazObject akan menjadi kasus yang dapat diterima dan pengembalian bool adalah ukuran menentukan kasus gagal dengan aman. Jika kode berikut adalah:

// calling code
BazObject baz = new BazObject();
bool result = fooObject.Foo(barID, out baz);

if (result) { 
    // do stuff with baz
    // where baz may be 
    // null without a 
    // thrown exception
}

Ini akan lebih masuk akal bagi saya untuk melakukannya dengan cara ini. Mungkin ini adalah metode yang seseorang sebelum Anda gunakan untuk menjamin bahwa baz dilewatkan dengan referensi tanpa memahami bagaimana sebenarnya parameter objek bekerja di C #.

Joel Etherton
sumber
Saya pikir itu ditulis oleh anggota tim saat ini. Mungkin itu sesuatu yang saya harus khawatirkan pada O_O
Wayne Molina
@Wayne M: Lebih sedikit kekhawatiran daripada peluang pelatihan yang potensial :)
Joel Etherton
1

Terkadang pola ini berguna ketika Anda, si penelepon, tidak seharusnya peduli apakah tipe yang dikembalikan adalah tipe referensi atau nilai. Jika Anda memanggil metode seperti itu untuk mengambil tipe nilai, tipe nilai itu akan membutuhkan nilai yang tidak valid (mis. Double.NaN), atau Anda perlu cara lain untuk menentukan kesuksesan.

Kevin Hsu
sumber
Itu masuk akal. Agak aneh, tapi itu kedengarannya seperti alasan yang masuk akal.
Wayne Molina
0

Idenya adalah mengembalikan nilai yang menunjukkan apakah proses berhasil, memungkinkan kode seperti ini:

Baz b;
if (fooObject.foo(id, out b)) {
   // do something with b
}
else {
   Error.screamAndRun("object lookup/whatever failed! AAaAAAH!");
}

Jika objek tidak boleh nol (yang muncul benar dalam contoh Anda), lebih baik dilakukan sebagai berikut:

Baz b = fooObject.foo(id);
if (b != null) {
   // do something with b
}
else {
   Error.screamAndRun("object lookup/whatever failed! AAaAAAH!");
}

Jika objek bisa nol, pengecualian adalah cara untuk pergi:

try {
   Baz b = fooObject.foo(id);
}
catch (BazException e) {
   Error.screamAndRun("object lookup/whatever failed! AAaAAAH!");
}

Ini adalah pola umum yang digunakan dalam C, di mana tidak ada pengecualian. Ini memungkinkan fungsi untuk mengembalikan kondisi kesalahan. Pengecualian umumnya merupakan solusi yang jauh lebih bersih.

Michael K.
sumber
Terutama karena kode itu sudah melempar pengecualian.
David Thornley