Mengapa Anda tidak menggunakan arahan 'menggunakan' dalam C #?

71

Standar pengkodean yang ada pada proyek C # besar mencakup aturan bahwa semua nama jenis sepenuhnya memenuhi syarat, melarang pekerjaan direktif 'menggunakan'. Jadi, daripada yang akrab:

using System.Collections.Generic;

.... other stuff ....

List<string> myList = new List<string>();

(Mungkin tidak mengherankan kalau itu varjuga dilarang.)

Saya berakhir dengan:

System.Collections.Generic.List<string> myList = new System.Collections.Generic.List<string>();

Itu peningkatan pengetikan 134%, dengan tidak ada peningkatan yang memberikan informasi bermanfaat. Dalam pandangan saya, 100% peningkatannya adalah noise (kekacauan) yang sebenarnya menghambat pemahaman.

Dalam 30+ tahun pemrograman, saya telah melihat standar yang diusulkan satu atau dua kali, tetapi tidak pernah diterapkan. Alasan di baliknya luput dari saya. Orang yang memaksakan standar itu tidak bodoh, dan saya tidak berpikir dia jahat. Yang membuat salah arah sebagai satu-satunya alternatif lain kecuali aku kehilangan sesuatu.

Pernahkah Anda mendengar standar seperti itu diberlakukan? Jika demikian, apa alasan di baliknya? Bisakah Anda memikirkan argumen selain "itu bodoh," atau "orang lain yang mempekerjakan using" yang mungkin meyakinkan orang ini untuk menghapus larangan ini?

Pemikiran

Alasan larangan ini adalah:

  1. Sulit untuk mengarahkan mouse ke atas nama untuk mendapatkan tipe yang sepenuhnya memenuhi syarat. Lebih baik untuk selalu memiliki tipe yang memenuhi syarat terlihat setiap saat.
  2. Cuplikan kode yang diemail tidak memiliki nama yang sepenuhnya memenuhi syarat, dan karenanya bisa sulit untuk dipahami.
  3. Saat melihat atau mengedit kode di luar Visual Studio (Notepad ++, misalnya), tidak mungkin untuk mendapatkan nama jenis yang sepenuhnya memenuhi syarat.

Pendapat saya adalah bahwa ketiga kasus ini jarang terjadi, dan membuat semua orang membayar harga kode yang berantakan dan kurang dapat dipahami hanya untuk mengakomodasi beberapa kasus langka adalah salah arah.

Masalah konflik namespace potensial, yang saya harapkan menjadi perhatian utama, bahkan tidak disebutkan. Itu terutama mengejutkan karena kami memiliki namespace MyCompany.MyProject.Core,, yang merupakan ide yang sangat buruk. Saya sudah lama tahu bahwa menamai sesuatu Systematau Coredalam C # adalah jalan cepat menuju kegilaan.

Seperti yang orang lain tunjukkan, konflik namespace mudah ditangani oleh refactoring, alias namespace, atau kualifikasi sebagian.

Jim Mischel
sumber
Komentar bukan untuk diskusi panjang; percakapan ini telah dipindahkan ke obrolan .
maple_shaft
1
Sebuah contoh dunia nyata mengapa itu bisa menjadi ide yang baik (tetapi di dunia C ++): jawaban untuk Mengapa "menggunakan namespace std;" dianggap praktik buruk? , di dekat "bahwa arahan dan deklarasi 'menggunakan' dilarang".
Peter Mortensen
Sebelum membaca bagian Penalaran, saya agak menebak alasan yang tidak taktis karena perusahaan saya memiliki aturan yang sama.
Gqqnbig

Jawaban:

69

Pertanyaan yang lebih luas:

Pernahkah Anda mendengar standar seperti itu diberlakukan? Jika demikian, apa alasan di baliknya?

Ya, saya pernah mendengar ini, dan menggunakan nama objek yang memenuhi syarat mencegah tabrakan nama. Meskipun jarang, ketika mereka terjadi, mereka bisa sangat sulit untuk mencari tahu.


Contoh: Jenis skenario itu mungkin lebih baik dijelaskan dengan contoh.

Katakanlah kita memiliki dua Lists<T>milik dua proyek terpisah.

System.Collections.Generic.List<T>
MyCorp.CustomCollections.Optimized.List<T>

Saat kami menggunakan nama objek yang sepenuhnya memenuhi syarat, jelas yang List<T>digunakan. Kejelasan itu jelas datang dengan mengorbankan kata-kata.

Dan Anda mungkin berdebat, "Tunggu! Tidak ada yang akan menggunakan dua daftar seperti itu!" Di situlah saya akan menunjukkan skenario perawatan.

Anda adalah modul tertulis Foountuk perusahaan Anda yang menggunakan perusahaan yang disetujui, dioptimalkan List<T>.

using MyCorp.CustomCollections.Optimized;

public class Foo {
    List<object> myList = ...;
}

Kemudian, pengembang baru memutuskan untuk memperpanjang pekerjaan yang telah Anda lakukan. Tidak mengetahui standar perusahaan, mereka memperbarui usingblok:

using MyCorp.CustomCollections.Optimized;
using System.Collections.Generic;

Dan Anda dapat melihat bagaimana semuanya menjadi buruk dengan tergesa-gesa.

Seharusnya sepele untuk menunjukkan bahwa Anda bisa memiliki dua kelas berpemilik dengan nama yang sama tetapi dalam ruang nama yang berbeda dalam proyek yang sama. Jadi ini bukan hanya masalah bertabrakan dengan .NET framework yang disediakan kelas.

MyCorp.WeightsAndLengths.Measurement();
MyCorp.TimeAndSpace.Measurement();

Kenyataannya:
Sekarang, apakah ini mungkin terjadi di sebagian besar proyek? Tidak terlalu. Tetapi ketika Anda mengerjakan proyek besar dengan banyak input, Anda melakukan yang terbaik untuk meminimalkan kemungkinan hal-hal meledak pada Anda.

Proyek besar dengan banyak tim yang berkontribusi adalah jenis binatang buas khusus di dunia aplikasi. Aturan yang tampaknya tidak masuk akal untuk proyek lain menjadi lebih relevan karena aliran input ke proyek dan kemungkinan bahwa mereka yang berkontribusi belum membaca pedoman proyek.

Ini juga dapat terjadi ketika dua proyek besar digabung menjadi satu. Jika kedua proyek memiliki kelas yang sama bernama, maka Anda akan melihat tabrakan ketika Anda mulai merujuk dari satu proyek ke yang lain. Dan proyek-proyek mungkin terlalu besar untuk refactor atau manajemen tidak akan menyetujui biaya untuk mendanai waktu yang dihabiskan untuk refactoring.


Alternatif:
Meskipun Anda tidak bertanya, ada baiknya menunjukkan bahwa ini bukan solusi yang bagus untuk masalah tersebut. Bukan ide yang baik untuk membuat kelas yang akan bertabrakan tanpa deklarasi namespace mereka.

List<T>, khususnya, harus diperlakukan sebagai kata yang dilindungi dan tidak digunakan sebagai nama untuk kelas Anda.

Demikian juga, ruang nama individu dalam proyek harus berusaha untuk memiliki nama kelas yang unik. Harus mencoba dan mengingat namespace mana yang Foo()sedang Anda kerjakan adalah overhead mental yang sebaiknya dihindari. Mengatakan dengan cara lain: memiliki MyCorp.Bar.Foo()dan MyCorp.Baz.Foo()akan membuat pengembang Anda tersandung dan sebaiknya dihindari.

Jika tidak ada yang lain, Anda bisa menggunakan ruang nama parsial untuk menyelesaikan ambiguitas. Misalnya, jika Anda benar-benar tidak dapat mengganti nama Foo()kelas mana pun Anda bisa menggunakan ruang nama parsial mereka:

Bar.Foo() 
Baz.Foo()

Alasan spesifik untuk proyek Anda saat ini:

Anda memperbarui pertanyaan Anda dengan alasan spesifik Anda diberikan untuk proyek Anda saat ini mengikuti standar itu. Mari kita lihat mereka dan benar-benar menyimpang dari jalan kelinci.

Sulit untuk mengarahkan mouse ke atas nama untuk mendapatkan tipe yang sepenuhnya memenuhi syarat. Lebih baik untuk selalu memiliki tipe yang memenuhi syarat terlihat setiap saat.

"Tidak praktis?" Um, tidak. Mungkin menjengkelkan. Memindahkan beberapa ons plastik untuk menggeser pointer di layar tidak merepotkan . Tapi saya ngelantur.

Alur penalaran ini tampaknya lebih seperti menutup-nutupi daripada hal lainnya. Begitu saja, saya kira bahwa kelas-kelas dalam aplikasi tersebut bernama lemah dan Anda harus mengandalkan namespace untuk mendapatkan informasi semantik yang sesuai dengan nama kelas.

Ini bukan pembenaran yang valid untuk nama kelas yang sepenuhnya memenuhi syarat, mungkin itu pembenaran yang valid untuk menggunakan nama kelas yang memenuhi syarat sebagian.

Cuplikan kode yang diemail tidak memiliki nama yang sepenuhnya memenuhi syarat, dan karenanya bisa sulit untuk dipahami.

Garis penalaran (lanjutan?) Ini memperkuat kecurigaan saya bahwa kelas saat ini tidak disebutkan namanya. Sekali lagi, memiliki nama kelas yang buruk bukanlah pembenaran yang baik untuk mengharuskan semuanya menggunakan nama kelas yang sepenuhnya memenuhi syarat. Jika nama kelas sulit untuk dipahami, ada jauh lebih banyak kesalahan daripada apa yang dapat diperbaiki oleh nama kelas yang memenuhi syarat.

Saat melihat atau mengedit kode di luar Visual Studio (Notepad ++, misalnya), tidak mungkin untuk mendapatkan nama jenis yang sepenuhnya memenuhi syarat.

Dari semua alasan, yang satu ini hampir membuat saya mengeluarkan minuman saya. Tapi sekali lagi, saya ngelantur.

Saya bertanya - tanya mengapa tim sering mengedit atau melihat kode di luar Visual Studio? Dan sekarang kita sedang melihat pembenaran yang cukup orthogonal untuk apa ruang nama dimaksudkan untuk menyediakan. Ini adalah argumen yang didukung perkakas sedangkan ruang nama ada untuk menyediakan struktur organisasi untuk kode.

Sepertinya proyek Anda sendiri mengalami konvensi penamaan yang buruk bersama dengan pengembang yang tidak mengambil keuntungan dari apa yang dapat disediakan oleh alat tersebut untuk mereka. Dan alih-alih menyelesaikan masalah-masalah aktual itu, mereka mencoba untuk menampar bantuan pita atas salah satu gejala dan membutuhkan nama kelas yang sepenuhnya memenuhi syarat. Saya pikir aman untuk mengkategorikan ini sebagai pendekatan yang salah arah.

Mengingat bahwa ada kelas-kelas dengan nama buruk, dan dengan asumsi Anda tidak dapat melakukan refactor, jawaban yang benar adalah dengan menggunakan Visual Studio IDE untuk keuntungan penuhnya. Pertimbangkan untuk menambahkan plugin seperti paket VS PowerTools. Kemudian, ketika saya melihat AtrociouslyNamedClass()saya dapat mengklik pada nama kelas, tekan F12 dan langsung dibawa ke definisi kelas untuk lebih memahami apa yang coba dilakukan. Demikian juga, saya dapat mengklik Shift-F12 untuk menemukan semua tempat dalam kode yang saat ini harus digunakan AtrociouslyNamedClass().

Mengenai masalah tooling luar - hal terbaik untuk dilakukan adalah menghentikannya. Jangan mengirim email bolak-balik jika tidak segera jelas apa yang dimaksud. Jangan gunakan alat lain di luar Visual Studio karena alat itu tidak memiliki kecerdasan seputar kode yang dibutuhkan tim Anda. Notepad ++ adalah alat yang luar biasa, tetapi tidak cocok untuk tugas ini.

Jadi saya setuju dengan penilaian Anda mengenai tiga pembenaran khusus yang Anda terima. Yang mengatakan, apa yang saya pikir Anda diberitahu adalah "Kami memiliki masalah mendasar pada proyek ini yang tidak dapat / tidak akan mengatasi dan ini adalah bagaimana kami 'memperbaiki' mereka." Dan itu jelas berbicara tentang masalah yang lebih dalam di dalam tim yang mungkin berfungsi sebagai tanda bahaya bagi Anda.


sumber
1
+1. Saya juga benar-benar mengalami beberapa tabrakan jahat di ruang nama internal kami. Baik dalam porting kode warisan ke proyek "2.0", dan hanya dengan beberapa nama kelas terpilih yang secara semantik valid dalam konteks namespace yang berbeda. Yang benar-benar rumit adalah bahwa dua hal dengan nama yang sama akan sering memiliki antarmuka yang sama, sehingga kompiler tidak akan mengeluh ... runtime Anda akan!
svidgen
23
Masalah ini diselesaikan dengan menggunakan alias namespace, bukan dengan memaksakan aturan konyol yang membutuhkan kode dengan berantakan dengan penggunaan namespace eksplisit.
David Arno
4
@ DavidArno - Saya tidak setuju dengan Anda. Namun, proyek besar mungkin tidak memiliki opsi itu. Saya hanya menunjukkan mengapa sebuah proyek besar dapat memaksakan aturan itu. Tapi saya tidak menganjurkan aturan. Hanya saja kadang-kadang diperlukan karena tim tidak memiliki opsi lain.
4
@ GlenH7 Anda mungkin ingin menentukan solusi lain yang tersedia dalam jawaban Anda. Aku benci melihat orang-orang menemukan ini dan berpikir, "Oh. Itu ide yang bagus!" +1 untuk tujuan yang tersisa dan pragmatis.
RubberDuck
3
@ Jimmischel - Kedengarannya seperti aturan yang digunakan sebagai penopang untuk sesuatu . Mungkin tidak mungkin untuk menentukan apa benda itu. Pada tingkat tinggi, ya, kadang-kadang ada justifikasi untuk tidak mengizinkan usingstetapi itu tidak terdengar seperti proyek Anda memenuhi syarat sebagai salah satu kasus tersebut.
16

Saya memang bekerja di satu perusahaan di mana mereka memiliki banyak kelas dengan nama yang sama, tetapi di perpustakaan kelas yang berbeda.

Sebagai contoh,

  • Pelanggan Domain
  • Warisan. Pelanggan
  • ServiceX.Customer
  • ViewModel.Customer
  • Database. Pelanggan

Desain yang buruk, Anda mungkin berkata, tetapi Anda tahu bagaimana hal-hal ini berkembang secara organik, dan apa alternatifnya - mengganti nama semua kelas untuk menyertakan namespace? Refactoring utama segalanya?

Bagaimanapun, ada beberapa tempat di mana proyek yang mereferensikan semua perpustakaan yang berbeda ini diperlukan untuk menggunakan beberapa versi kelas.

Dengan usinglangsung di atas, ini adalah rasa sakit yang hebat di pantat, ReSharper akan melakukan hal-hal aneh untuk mencoba dan membuatnya bekerja, menulis ulang usingarahan dengan cepat, Anda akan mendapatkan Pelanggan-tidak-bisa-dilemparkan-sebagai- Kesalahan gaya pelanggan dan tidak tahu jenis mana yang benar.

Jadi, setidaknya memiliki namespace parsial,

var cust = new Domain.Customer

atau

public void Purchase(ViewModel.Customer customer)

sangat meningkatkan keterbacaan kode dan membuatnya eksplisit objek yang Anda inginkan.

Itu bukan standar pengkodean, itu bukan namespace lengkap, dan itu tidak diterapkan ke semua kelas sebagai standar.

Ewan
sumber
13
"Apa alternatifnya, ganti nama semua kelas untuk memasukkan namespace? refactoring utama segalanya?" Ya, itulah alternatif (yang benar).
David Arno
2
Hanya dalam jangka pendek. Ini jauh lebih murah daripada menggunakan ruang nama eksplisit dalam kode dalam jangka panjang.
David Arno
3
Senang menerima argumen itu, selama Anda membayar saya secara tunai bukan saham.
Ewan
8
@ David: Tidak, BUKAN alternatif yang benar. Kualifikasi penuh baik semua pengidentifikasi atau setidaknya yang benar-benar bertabrakan adalah cara yang benar dan alasan mengapa ruang nama ada di tempat pertama adalah untuk memberikan tabrakan ketika nama yang sama digunakan dalam modul yang berbeda. Intinya adalah bahwa beberapa modul mungkin berasal dari pihak ke-3, basis kode mana yang tidak dapat Anda modifikasi. Jadi apa yang Anda lakukan, ketika pengidentifikasi dari dua perpustakaan pihak ketiga yang berbeda bertabrakan dan Anda perlu menggunakan kedua perpustakaan, tetapi tidak dapat memperbaiki keduanya? Ruang nama ada, karena refactoring tidak selalu mungkin.
Kaiserludi
4
@CarlSixsmith, jika seseorang berlangganan pandangan Martin Fowler tentang refactoring, maka jika perubahan berdampak pada pengguna, maka Anda benar, itu bukan refactoring; itu bentuk lain dari restrukturisasi. Ini menimbulkan dua poin: 1. Apakah semua metode publik secara otomatis bagian dari API? 2. Fowler sendiri mengakui bahwa dia sedang berjuang keras dalam memperebutkan definisi ini, dengan semakin banyak orang menggunakan istilah "refactoring" untuk berarti restrukturisasi umum, termasuk perubahan publik.
David Arno
7

Tidak ada gunanya menjadi terlalu bertele-tele atau terlalu pendek: seseorang harus menemukan tengah emas dalam cukup rinci untuk mencegah bug halus, sambil menghindari untuk menulis terlalu banyak kode.

Dalam C #, contohnya adalah nama-nama argumen. Saya mengalami beberapa kali masalah di mana saya menghabiskan berjam-jam mencari solusi, sementara gaya penulisan yang lebih verbal dapat mencegah bug di tempat pertama. Masalahnya terdiri dari kesalahan dalam urutan argumen. Misalnya, apakah itu cocok untuk Anda?

if (...) throw new ArgumentNullException("name", "The name should be specified.");
if (...) throw new ArgumentException("name", "The name contains forbidden characters.");

Pada pandangan pertama, itu terlihat baik-baik saja, tetapi ada bug. Tanda tangan konstruktor pengecualian adalah:

public ArgumentException(string message, string paramName)
public ArgumentNullException(string paramName, string message)

Memperhatikan urutan argumen?

Dengan mengadopsi gaya yang lebih verbose di mana nama argumen ditentukan setiap kali metode menerima dua atau lebih argumen dari jenis yang sama , kami mencegah kesalahan terjadi lagi, dan karenanya:

if (...) throw new ArgumentNullException(paramName: "name", message: "The name should be specified.");
if (...) throw new ArgumentException(paramName: "name", message: "The name contains forbidden characters.");

jelas lebih panjang untuk ditulis, tetapi menghasilkan lebih sedikit waktu terbuang untuk debugging.

Didorong ke ekstrem, orang bisa memaksa untuk menggunakan argumen bernama di mana-mana , termasuk dalam metode seperti di doSomething(int, string)mana tidak ada cara untuk mencampur urutan parameter. Ini mungkin akan membahayakan proyek dalam jangka panjang, menghasilkan lebih banyak kode tanpa manfaat mencegah bug yang saya jelaskan di atas.

Dalam kasus Anda, motivasi di balik “Tidak ada using” aturan adalah bahwa ia harus membuat lebih sulit untuk menggunakan tipe yang salah, seperti MyCompany.Something.List<T>vs System.Collections.Generic.List<T>.

Secara pribadi, saya akan menghindari aturan seperti itu karena, IMHO, biaya sulit untuk membaca kode jauh di atas manfaat dari risiko yang sedikit berkurang dari penyalahgunaan jenis yang salah, tetapi setidaknya, ada alasan yang sah untuk aturan ini.

Arseni Mourzenko
sumber
Situasi seperti ini dapat dideteksi oleh alat. ReSharper menandai paramNamedengan InvokerParameterNameatribut dan mengeluarkan peringatan jika tidak ada parameter seperti itu.
Johnbot
2
@ Johnbot: mereka pasti bisa, tetapi dalam jumlah kasus yang sangat terbatas. Bagaimana jika saya punya SomeBusiness.Something.DoStuff(string name, string description)? Tidak ada alat yang cukup pintar untuk memahami apa itu nama dan apa itu deskripsi.
Arseni Mourzenko
@ArseniMourzenko dalam hal ini bahkan manusia perlu mengambil waktu untuk mencari tahu apakah doa itu benar atau tidak.
Gqqnbig
6

Namespaces memberi kode struktur hierarkis, memungkinkan untuk nama yang lebih pendek.

   MyCompany.Headquarters.Team.Leader
   MyCompany.Warehouse.Team.Leader

Jika Anda mengizinkan kata kunci "menggunakan", ada serangkaian acara ...

  • Semua orang benar-benar menggunakan satu namespace global
    (karena mereka menyertakan setiap namespace yang mungkin mereka inginkan)
  • Orang-orang mulai mendapatkan bentrokan nama, atau
  • khawatir tentang bentrokan nama.

Jadi untuk menghindari risiko, semua orang mulai menggunakan nama yang sangat panjang:

   HeadquartersTeamLeader
   WarehouseTeamLeader

Situasi meningkat ...

  • Orang lain mungkin sudah memilih nama, jadi gunakan yang lebih lama.
  • Nama semakin lama.
  • Panjangnya dapat melebihi 60 karakter, tanpa maksimum - itu menjadi konyol.

Saya telah melihat ini terjadi, sehingga dapat bersimpati dengan perusahaan yang melarang "menggunakan".


sumber
11
Larangan usingadalah opsi nuklir. Alias ​​Namespace dan kualifikasi parsial lebih disukai.
Jim Mischel
1

Masalahnya usingadalah bahwa itu secara ajaib menambahkan ke namespace tanpa pernyataan eksplisit. Ini jauh lebih dari masalah bagi programmer pemeliharaan yang menemukan sesuatu seperti List<>dan tanpa terbiasa dengan domain, tidak tahu usingklausa mana yang telah memperkenalkannya.

Masalah serupa terjadi di Jawa jika seseorang menulis import Java.util.*;daripada import Java.util.List;misalnya; deklarasi terakhir mendokumentasikan nama apa yang telah diperkenalkan sehingga ketika List<>muncul kemudian di sumbernya, asalnya diketahui dari importdeklarasi.

Michael Montenero
sumber
6
Padahal benar bahwa seseorang yang tidak terbiasa dengan domain akan mengalami beberapa kesulitan, yang harus dia lakukan adalah mengarahkan mouse di atas nama tipe dan dia akan mendapatkan nama yang sepenuhnya memenuhi syarat. Atau dia dapat mengklik kanan dan langsung menuju definisi tipe. Untuk jenis .NET Framework, dia mungkin sudah tahu di mana semuanya berada. Untuk tipe khusus domain, mungkin perlu seminggu baginya untuk merasa nyaman. Pada saat itu, nama-nama yang memenuhi syarat hanyalah kebisingan yang menghambat pemahaman.
Jim Mischel
Jim .. Saya baru saja mencoba mengarahkan mouse ke definisi dan tidak berhasil. Apakah Anda mempertimbangkan kemungkinan bahwa beberapa programmer di dunia tidak menggunakan Microsoft IDE? Bagaimanapun, penghitung Anda tidak membatalkan maksud saya, bahwa sumber dengan menggunakan tidak lagi mendokumentasikan diri sendiri
Michael Montenero
2
Ya, ada orang yang bekerja di C # yang menghambat diri sendiri dengan tidak menggunakan alat terbaik yang tersedia. Dan, sementara argumen bahwa kualifikasi penuh adalah "mendokumentasikan diri sendiri" memiliki beberapa kelebihan, kode tersebut memiliki biaya yang sangat besar dalam keterbacaan. Semua kode asing menciptakan kebisingan yang mengaburkan bagian-bagian dari kode yang benar-benar penting.
Jim Mischel