Apakah menegaskan kejahatan? [Tutup]


199

The Gopencipta bahasa tulis :

Go tidak memberikan pernyataan. Mereka tidak diragukan lagi nyaman, tetapi pengalaman kami bahwa pemrogram menggunakannya sebagai penopang untuk menghindari berpikir tentang penanganan dan pelaporan kesalahan yang tepat. Penanganan kesalahan yang tepat berarti bahwa server melanjutkan operasi setelah kesalahan non-fatal alih-alih mogok. Pelaporan kesalahan yang tepat berarti bahwa kesalahan itu langsung dan to the point, menyelamatkan programmer dari menafsirkan jejak kecelakaan besar. Kesalahan yang tepat sangat penting ketika programmer melihat kesalahan tidak terbiasa dengan kode.

Apa pendapat Anda tentang ini?


4
singgung: Go adalah bahasa dengan pendapat luar biasa. Ini tidak selalu merupakan hal yang buruk. Namun, itu berarti Anda harus mengambil pendapatnya dengan sebutir garam yang lebih besar. Ini juga berarti bahwa jika Anda tidak setuju, Anda akan menggertakkan gigi saat menggunakan bahasa tersebut. Sebagai bukti bagaimana Go berpegang teguh pada pendapatnya meskipun ada kenyataan, pertimbangkan bahwa Anda perlu menggunakan keajaiban refleksi untuk menentukan apakah dua koleksi itu sama.
allyourcode

@allyourcode Jika Anda merujuk reflect.DeepEqual, Anda tentu tidak membutuhkannya . Memang nyaman, tetapi dengan mengorbankan kinerja (unit test adalah kasus penggunaan yang baik). Jika tidak, Anda dapat menerapkan pemeriksaan kesetaraan apa pun yang sesuai untuk "koleksi" Anda tanpa terlalu banyak kesulitan.
Igor Dubinskiy

1
Tidak, bukan itu yang saya bicarakan. Tidak ada yang namanya slice1 == slice2 tanpa refleksi. Semua bahasa lain memiliki setara dengan operasi super dasar ini. Satu-satunya alasan Go tidak berprasangka.
allyourcode

Anda dapat membandingkan dua irisan tanpa refleksi menggunakan forloop in Go (seperti C). Ini akan benar-benar menyenangkan untuk memiliki operasi slice generik, meskipun perbandingan jadi rumit ketika pointer dan struct yang terlibat.
kbolino

Jawaban:


321

Tidak, tidak ada yang salah assertselama Anda menggunakannya sebagaimana dimaksud.

Artinya, itu seharusnya untuk menangkap kasus yang "tidak dapat terjadi", selama debugging, sebagai lawan dari penanganan kesalahan normal.

  • Menegaskan: Kegagalan dalam logika program itu sendiri.
  • Penanganan Kesalahan: Input yang salah atau status sistem bukan karena bug dalam program.

109

Tidak, tidak gotojuga asserttidak jahat. Namun keduanya bisa disalahgunakan.

Menegaskan adalah untuk pemeriksaan kewarasan. Hal-hal yang seharusnya mematikan program jika tidak benar. Bukan untuk validasi atau sebagai pengganti untuk penanganan kesalahan.


bagaimana cara menggunakan gotodengan bijak?
ar2015

1
@ ar2015 Temukan salah satu pola dibuat-buat yang tidak masuk akal yang beberapa orang sarankan untuk hindari gotokarena alasan agama semata, kemudian gunakan saja gotodaripada mengaburkan apa yang Anda lakukan. Dengan kata lain: jika Anda dapat membuktikan bahwa Anda benar-benar membutuhkan goto, dan satu-satunya alternatif adalah memberlakukan banyak perancah tidak berguna yang pada akhirnya melakukan hal yang sama tetapi tanpa mengubah Polisi Goto ... maka gunakan saja goto. Tentu saja, prasyarat untuk hal ini adalah "jika Anda dapat membuktikan bahwa Anda benar-benar membutuhkan goto". Seringkali, orang tidak. Itu masih tidak berarti itu pada dasarnya Hal Buruk.
underscore_d

2
gotodigunakan di kernel linux untuk pembersihan kode
malat

61

Dengan logika itu, breakpoint juga jahat.

Pemberitahuan harus digunakan sebagai bantuan debugging, dan tidak ada yang lain. "Evil" adalah ketika Anda mencoba menggunakannya alih - alih penanganan kesalahan.

Asserts ada untuk membantu Anda, programmer, mendeteksi dan memperbaiki masalah yang tidak boleh ada dan memverifikasi bahwa asumsi Anda tetap benar.

Mereka tidak ada hubungannya dengan penanganan kesalahan, tetapi sayangnya, beberapa programmer menyalahgunakannya, dan kemudian menyatakan mereka "jahat".


40

Saya suka menggunakan banyak menegaskan. Saya merasa sangat berguna ketika saya membangun aplikasi untuk pertama kalinya (mungkin untuk domain baru). Alih-alih melakukan pengecekan error yang sangat mewah (bahwa saya akan mempertimbangkan optimasi prematur) saya kode cepat dan saya menambahkan banyak menegaskan. Setelah saya tahu lebih banyak tentang cara kerja saya menulis ulang dan menghapus beberapa dari konfirmasi dan mengubahnya untuk penanganan kesalahan yang lebih baik.

Karena menegaskan saya menghabiskan banyak waktu lebih sedikit program coding / debugging.

Saya juga memperhatikan bahwa pernyataan tersebut membantu saya memikirkan banyak hal yang dapat merusak program saya.


31

Sebagai informasi tambahan, go menyediakan fungsi bawaan panic. Ini dapat digunakan sebagai pengganti assert. Misalnya

if x < 0 {
    panic("x is less than 0");
}

panicakan mencetak jejak tumpukan, jadi dalam beberapa cara ia memiliki tujuan assert.


30

Mereka harus digunakan untuk mendeteksi bug dalam program. Input pengguna tidak buruk.

Jika digunakan dengan benar, mereka tidak jahat.


13

Ini banyak muncul, dan saya pikir satu masalah yang membuat pembelaan terhadap pernyataan membingungkan adalah bahwa mereka sering didasarkan pada pengecekan argumen. Jadi pertimbangkan contoh berbeda ketika Anda mungkin menggunakan pernyataan:

build-sorted-list-from-user-input(input)

    throw-exception-if-bad-input(input)

    ...

    //build list using algorithm that you expect to give a sorted list

    ...

    assert(is-sorted(list))

end

Anda menggunakan pengecualian untuk input karena Anda berharap terkadang Anda akan mendapatkan input yang buruk. Anda menegaskan bahwa daftar diurutkan untuk membantu Anda menemukan bug dalam algoritme Anda, yang menurut definisi tidak Anda harapkan. Pernyataan ini hanya ada dalam versi debug, jadi meskipun biayanya mahal, Anda tidak keberatan melakukannya di setiap permintaan rutin.

Anda masih harus menguji unit kode produksi Anda, tetapi itu cara yang berbeda dan saling melengkapi untuk memastikan kode Anda benar. Tes unit memastikan rutinitas Anda sesuai dengan antarmuka, sementara pernyataan adalah cara yang lebih halus untuk memastikan implementasi Anda melakukan apa yang Anda harapkan.


8

Penegasan tidak jahat tetapi mereka dapat dengan mudah disalahgunakan. Saya setuju dengan pernyataan bahwa "pernyataan sering digunakan sebagai penopang untuk menghindari berpikir tentang penanganan dan pelaporan kesalahan yang tepat". Saya sudah sering melihat ini.

Secara pribadi, saya suka menggunakan pernyataan karena mereka mendokumentasikan asumsi yang mungkin saya buat ketika menulis kode saya. Jika asumsi ini rusak saat mempertahankan kode, masalahnya dapat dideteksi selama pengujian. Namun, saya membuat titik untuk menghapus setiap pernyataan dari kode saya ketika melakukan membangun produksi (yaitu, menggunakan #ifdefs). Dengan menghapus pernyataan dalam produksi, saya menghilangkan risiko orang menyalahgunakannya sebagai penopang.

Ada juga masalah lain dengan pernyataan. Pernyataan hanya diperiksa pada saat run-time. Namun seringkali pemeriksaan yang ingin Anda lakukan bisa dilakukan pada waktu kompilasi. Lebih baik mendeteksi masalah pada waktu kompilasi. Untuk programmer C ++, boost menyediakan BOOST_STATIC_ASSERT yang memungkinkan Anda melakukan ini. Untuk programmer C, artikel ini ( teks tautan ) menjelaskan teknik yang dapat digunakan untuk melakukan pernyataan pada waktu kompilasi.

Singkatnya, aturan praktis yang saya ikuti adalah: Jangan menggunakan pernyataan dalam membangun produksi dan, jika mungkin, hanya menggunakan pernyataan untuk hal-hal yang tidak dapat diverifikasi pada waktu kompilasi (yaitu, harus diperiksa pada saat run-time).


5

Saya akui telah menggunakan konfirmasi tanpa mempertimbangkan pelaporan kesalahan yang tepat. Namun, itu tidak menghilangkan bahwa mereka sangat membantu ketika digunakan dengan benar.

Mereka sangat berguna jika Anda ingin mengikuti prinsip "Kecelakaan Dini". Misalnya, Anda menerapkan mekanisme penghitungan referensi. Di lokasi tertentu dalam kode Anda, Anda tahu bahwa refcount harus nol atau satu. Dan juga anggaplah bahwa jika refcount salah, program tidak akan crash segera tetapi selama loop pesan berikutnya pada titik mana akan sulit untuk mengetahui mengapa ada yang salah. Penegasan akan sangat membantu dalam mendeteksi kesalahan lebih dekat ke asalnya.


5

Saya lebih suka menghindari kode yang melakukan hal berbeda dalam debug dan rilis.

Membobol debugger pada kondisi dan memiliki semua info file / baris berguna, juga ekspresi yang tepat dan nilai yang tepat.

Memiliki pernyataan yang akan "mengevaluasi kondisi hanya dalam debug" mungkin merupakan pengoptimalan kinerja, dan karenanya, hanya berguna dalam 0,0001% program - di mana orang tahu apa yang mereka lakukan. Dalam semua kasus lain, ini berbahaya, karena ekspresi sebenarnya dapat mengubah status program:

assert(2 == ShroedingersCat.GetNumEars()); akan membuat program melakukan berbagai hal dalam debug dan rilis.

Kami telah mengembangkan satu set makro pernyataan yang akan mengeluarkan pengecualian, dan melakukannya dalam versi debug dan rilis. Misalnya, THROW_UNLESS_EQ(a, 20);akan melempar pengecualian dengan pesan apa () yang memiliki kedua file, baris, dan nilai aktual a, dan seterusnya. Hanya makro yang memiliki kekuatan untuk ini. Pengawak mungkin dikonfigurasikan untuk memecahkan pada 'throw' dari tipe pengecualian spesifik.


4
90% persen statistik yang digunakan dalam argumen salah.
João Portela

5

Saya sangat tidak suka menegaskan. Namun saya tidak akan mengatakan bahwa mereka jahat.

Pada dasarnya pernyataan akan melakukan hal yang sama dengan pengecualian yang tidak dicentang, satu-satunya pengecualian adalah bahwa pernyataan (biasanya) tidak boleh disimpan untuk produk akhir.

Jika Anda membangun jaring pengaman untuk diri Anda sendiri saat debugging dan membangun sistem mengapa Anda menolak jaring pengaman ini untuk pelanggan Anda, atau meja bantuan dukungan Anda, atau siapa saja yang akan menggunakan perangkat lunak yang sedang Anda buat. Gunakan pengecualian secara eksklusif untuk situasi yang menegaskan dan luar biasa. Dengan membuat hierarki pengecualian yang sesuai, Anda akan dapat membedakan satu dari yang lainnya dengan sangat cepat. Kecuali saat ini pernyataan tetap ada dan dapat memberikan informasi berharga jika terjadi kegagalan yang seharusnya hilang.

Jadi saya sepenuhnya memahami pencipta Go dengan menghapus sama sekali dan memaksa programmer untuk menggunakan pengecualian untuk menangani situasi. Ada penjelasan sederhana untuk ini, pengecualian hanyalah mekanisme yang lebih baik untuk pekerjaan itu, mengapa tetap dengan ingatan kuno?


Go tidak memiliki pengecualian. Alasan yang biasa untuk menggunakan suatu pernyataan alih-alih pengecualian adalah karena Anda ingin hal itu dihapuskan dalam penerapan karena alasan kinerja. Pernyataan tidak kuno. Maaf kedengarannya kasar, tetapi jawaban ini tidak terlalu membantu pertanyaan awal, juga tidak benar.
Nir Friedman


3

Saya baru-baru ini mulai menambahkan beberapa pernyataan ke kode saya, dan ini adalah bagaimana saya telah melakukannya:

Saya secara mental membagi kode saya menjadi kode batas dan kode internal. Kode batas adalah kode yang menangani input pengguna, membaca file, dan mendapatkan data dari jaringan. Dalam kode ini, saya meminta input dalam satu loop yang hanya keluar ketika input valid (dalam hal input pengguna interaktif), atau melemparkan pengecualian dalam kasus file / data rusak jaringan yang tidak dapat dipulihkan.

Kode internal adalah segalanya. Misalnya, fungsi yang menetapkan variabel di kelas saya mungkin didefinisikan sebagai

void Class::f (int value) {
    assert (value < end);
    member = value;
}

dan fungsi yang mendapat input dari jaringan mungkin terbaca seperti itu:

void Class::g (InMessage & msg) {
    int const value = msg.read_int();
    if (value >= end)
        throw InvalidServerData();
    f (value);
}

Ini memberi saya dua lapis cek. Apa pun di mana data ditentukan pada saat run-time selalu mendapatkan pengecualian atau penanganan kesalahan langsung. Namun, pemeriksaan tambahan Class::fdengan assertpernyataan itu berarti bahwa jika beberapa kode internal pernah menelepon Class::f, saya masih memiliki pemeriksaan kewarasan. Kode internal saya mungkin tidak memberikan argumen yang valid (karena saya mungkin telah menghitung valuedari beberapa rangkaian fungsi yang kompleks), jadi saya suka memiliki pernyataan dalam fungsi pengaturan untuk mendokumentasikan bahwa terlepas dari siapa yang memanggil fungsi, valuetidak boleh lebih besar dari atau sama dengan end.

Ini sepertinya cocok dengan apa yang saya baca di beberapa tempat, bahwa pernyataan tidak mungkin dilanggar dalam program yang berfungsi dengan baik, sementara pengecualian harus untuk kasus luar biasa dan kesalahan yang masih mungkin. Karena secara teori saya memvalidasi semua input, seharusnya tidak mungkin pernyataan saya dipicu. Jika ya, program saya salah.


2

Jika pernyataan yang Anda bicarakan berarti bahwa program muntah dan kemudian ada, pernyataan bisa sangat buruk. Ini bukan untuk mengatakan bahwa mereka selalu hal yang salah untuk digunakan, mereka adalah konstruksi yang sangat mudah disalahgunakan. Mereka juga memiliki banyak alternatif yang lebih baik. Hal-hal seperti itu adalah kandidat yang baik untuk disebut jahat.

Sebagai contoh, modul pihak ke-3 (atau modul apa saja) seharusnya hampir tidak pernah keluar dari program panggilan. Ini tidak memberi pemrogram panggilan kontrol atas risiko apa yang harus diambil oleh program pada saat itu. Dalam banyak kasus, data sangat penting sehingga bahkan menyimpan data yang rusak lebih baik daripada kehilangan data itu. Pernyataan dapat memaksa Anda kehilangan data.

Beberapa alternatif untuk menegaskan:

  • Menggunakan debugger,
  • Konsol / database / logging lainnya
  • Pengecualian
  • Jenis penanganan kesalahan lainnya

Beberapa referensi:

Bahkan orang-orang yang menganjurkan untuk menegaskan berpikir mereka hanya boleh digunakan dalam pembangunan dan bukan dalam produksi:

Orang ini mengatakan bahwa pernyataan harus digunakan ketika modul berpotensi merusak data yang tetap ada setelah pengecualian dilemparkan: http://www.advogato.org/article/949.html . Ini tentu saja merupakan poin yang masuk akal, namun, modul eksternal tidak boleh menentukan seberapa penting data yang rusak untuk program panggilan (dengan keluar "untuk" mereka). Cara yang tepat untuk menangani ini adalah dengan melemparkan pengecualian yang membuatnya jelas bahwa program sekarang mungkin dalam keadaan tidak konsisten. Dan karena program yang baik sebagian besar terdiri dari modul (dengan sedikit kode lem di executable utama), menegaskan hampir selalu hal yang salah untuk dilakukan.


1

assert sangat berguna dan dapat menyelamatkan Anda dari banyak kemunduran saat kesalahan tak terduga terjadi dengan menghentikan program pada tanda-tanda awal masalah.

Di sisi lain, sangat mudah disalahgunakan assert.

int quotient(int a, int b){
    assert(b != 0);
    return a / b;
}

Versi yang tepat dan benar akan seperti:

bool quotient(int a, int b, int &result){
    if(b == 0)
        return false;

    result = a / b;
    return true;
}

Jadi ... dalam jangka panjang ... dalam gambaran besar ... Saya harus setuju bahwa assertdapat disalahgunakan. Saya selalu melakukannya.


1

assert sedang disalahgunakan untuk penanganan kesalahan karena kurang mengetik.

Jadi sebagai perancang bahasa, mereka seharusnya melihat bahwa penanganan kesalahan yang tepat dapat dilakukan dengan pengetikan yang lebih rendah. Tidak termasuk menegaskan karena mekanisme pengecualian Anda bertele-tele bukanlah solusi. Oh, tunggu, Go juga tidak memiliki pengecualian. Sangat buruk :)


1
Tidak terlalu buruk :-) Pengecualian atau tidak, pernyataan atau tidak, penggemar Go masih berbicara tentang seberapa pendek kode.
Moshe Revah

1

Saya merasa seperti menendang kepala penulis ketika saya melihat itu.

Saya menggunakan menegaskan sepanjang waktu dalam kode dan akhirnya menggantikannya semua ketika saya menulis lebih banyak kode. Saya menggunakannya ketika saya belum menulis logika yang diperlukan dan ingin diberitahu ketika saya mengalami kode bukannya menulis pengecualian yang akan dihapus karena proyek semakin dekat dengan penyelesaian.

Pengecualian juga berbaur dengan kode produksi lebih mudah yang saya sukai. Pernyataan lebih mudah diperhatikan daripadathrow new Exception("Some generic msg or 'pretend i am an assert'");


1

Masalah saya dengan jawaban-jawaban ini adalah menegaskan tidak ada yang secara jelas menentukan apa yang membuatnya berbeda dari kesalahan fatal yang biasa , dan mengapa sebuah pernyataan tidak dapat menjadi bagian dari pengecualian . Sekarang, dengan ini dikatakan, bagaimana jika pengecualian tidak pernah ditangkap? Apakah itu membuatnya menjadi pernyataan nomenklatur? Dan, mengapa Anda ingin memaksakan pembatasan dalam bahasa yang dapat dikemukakan pengecualian / tidak ada / dapat menangani?


Jika Anda melihat jawaban saya. Penggunaan saya adalah untuk membedakan 'pengecualian' (menegaskan) bahwa saya ingin menyingkirkan digunakan untuk debugging vs pengecualian yang saya simpan. Mengapa saya ingin menyingkirkan mereka? karena tanpa mereka bekerja itu tidak akan lengkap. Contohnya adalah jika saya menangani 3 kasus dan yang ke-4 adalah todo. Saya dapat dengan mudah mencari menegaskan untuk menemukan mereka dalam kode dan tahu itu tidak lengkap daripada menggunakan pengecualian yang mungkin secara tidak sengaja tertangkap (oleh programmer lain) atau sulit untuk mengetahui apakah saya pengecualian atau logika memeriksa bahwa saya harus menyelesaikan dalam kode.

Di mata saya, ini adalah ide yang buruk, pada tingkat yang sama dengan kelas "tertutup" dan untuk alasan yang sama. Anda mengasumsikan pengecualian yang ingin Anda pertahankan dapat diterima untuk penggunaan kode Anda yang belum Anda ketahui. Semua pengecualian melewati saluran yang sama, dan jika pengguna tidak ingin menangkapnya, ia dapat memilih untuk tidak melakukannya. Jika dia melakukannya, dia harus memiliki kemampuan juga. Either way, Anda hanya membuat asumsi atau mendorong praktik Anda dengan konsep seperti pernyataan.
Evan Carroll

Saya memutuskan contoh skenario yang terbaik. Inilah yang sederhana. int func (int i) {if (i> = 0) {console.write ("Angka positif {0}", i); } else {assert (false); // untuk malas melakukan ATM negatif} return i * 2; <- Bagaimana saya melakukan ini tanpa menegaskan dan pengecualian benar-benar lebih baik? dan ingat, kode ini akan diterapkan sebelum dirilis.

Tentu saja pengecualian lebih baik, katakanlah saya mengambil input pengguna dan menelepon func()dengan nomor negatif. Sekarang, tiba-tiba dengan pernyataan Anda, Anda telah menarik karpet dari bawah saya dan tidak memberi saya kesempatan untuk pulih, daripada dengan sopan memberi tahu saya apa yang saya minta tidak bisa dilakukan. Tidak ada yang salah dengan menuduh program itu berperilaku tidak pantas dan mengeluarkannya kutipan: masalahnya adalah Anda mengaburkan tindakan menegakkan hukum, dan menghukum penjahat.
Evan Carroll

Itulah intinya, Anda TIDAK HARUS mengatakan apa yang ingin dilakukan pengguna. Aplikasi harus diakhiri dengan pesan kesalahan dan siapa pun yang melakukan debug akan mengingat sesuatu yang HARUS DILAKUKAN tetapi tidak. Anda TIDAK ingin ditangani. Anda ingin pengakhiran dan diingatkan bahwa Anda tidak menangani kasus itu. dan sangat mudah untuk melihat kasus-kasus apa yang tersisa karena semua yang perlu Anda lakukan adalah mencari kode yang tegas. Menangani kesalahan akan salah karena kode harus dapat melakukannya setelah siap untuk diproduksi. Mengizinkan programmer menangkapnya dan melakukan hal lain adalah salah.

1

Ya, menegaskan itu jahat.

Seringkali mereka digunakan di tempat-tempat di mana penanganan kesalahan yang tepat harus digunakan. Biasakan menulis penanganan kesalahan kualitas produksi yang tepat sejak awal!

Biasanya mereka menghalangi penulisan unit test (kecuali jika Anda menulis pernyataan kustom yang berinteraksi dengan harness tes Anda). Ini sering karena mereka digunakan di mana penanganan kesalahan yang tepat harus digunakan.

Sebagian besar mereka dikompilasi dari rilis build yang berarti bahwa tidak ada "pengujian" mereka yang tersedia ketika Anda menjalankan kode yang sebenarnya Anda rilis; mengingat bahwa dalam situasi multi-utas masalah terburuk seringkali hanya muncul dalam kode rilis ini bisa buruk.

Terkadang mereka adalah penopang untuk desain yang rusak; yaitu desain kode memungkinkan pengguna untuk memanggilnya dengan cara yang seharusnya tidak dipanggil dan menyatakan "mencegah" ini. Perbaiki desain!

Saya menulis tentang ini lebih lanjut di blog saya pada tahun 2005 di sini: http://www.lenholgate.com/blog/2005/09/assert-is-evil.html


0

Tidak seburuk yang umumnya kontraproduktif. Ada pemisahan antara pemeriksaan kesalahan permanen dan debugging. Menegaskan membuat orang berpikir bahwa semua debugging harus permanen dan menyebabkan masalah keterbacaan yang besar ketika banyak digunakan. Penanganan kesalahan permanen harus lebih baik daripada yang diperlukan, dan karena menegaskan menyebabkan kesalahan sendiri, itu adalah praktik yang cukup dipertanyakan.


5
Menegaskan baik untuk menyatakan pra-kondisi di bagian atas fungsi, dan jika ditulis dengan jelas, bertindak sebagai bagian dari dokumentasi fungsi.
Peter Cordes

0

saya tidak pernah menggunakan assert (), contoh biasanya menunjukkan sesuatu seperti ini:

int* ptr = new int[10];
assert(ptr);

Ini buruk, saya tidak pernah melakukan ini, bagaimana jika permainan saya mengalokasikan sekelompok monster? mengapa saya harus menghentikan permainan, alih-alih Anda harus menangani kesalahan dengan anggun, jadi lakukan sesuatu seperti:

CMonster* ptrMonsters = new CMonster[10];
if(ptrMonsters == NULL) // or u could just write if(!ptrMonsters)
{
    // we failed allocating monsters. log the error e.g. "Failed spawning 10 monsters".
}
else
{
    // initialize monsters.
}

14
newtidak pernah kembali nullptr, ia melempar.
David Stone

Perhatikan bahwa Anda dapat menggunakan std :: nothrow untuk itu.
tandai dan
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.