Apakah C # menjadi lebih sulit untuk dibaca?

15

Ketika C # telah berkembang, banyak fitur bahasa telah ditambahkan. Ini telah sampai pada titik di mana itu menjadi tidak dapat dibaca untuk saya.

Sebagai contoh, pertimbangkan snip kode berikut dari kode Caliburn.Micro di sini :

            container = CompositionHost.Initialize(
                   new AggregateCatalog(
                      AssemblySource.Instance.
                             Select(x => new AssemblyCatalog(x))
                               .OfType<ComposablePartCatalog>()
                )
            );

Sekarang, ini hanyalah contoh kecil.

Saya punya sejumlah pertanyaan:

  • Apakah ini masalah yang umum atau diketahui?
  • Apakah komunitas C # menemukan hal yang sama?
  • Apakah ini masalah dengan bahasa, atau apakah itu gaya yang digunakan oleh pengembang?

Apakah ada solusi sederhana untuk lebih memahami kode orang lain dan menghindari penulisan kode dengan cara ini?

Steven Evers
sumber
6
Ini adalah fungsi lambda, mereka sulit dibaca sampai Anda benar-benar mendapatkannya.
Ryathal
9
Saya pikir Anda benar-benar tidak tahu sintaksis seperti yang Anda kira. Dengan latihan membaca contoh Anda sederhana.
ChaosPandion
6
@Thomas Owens: Holy man kotoran, setidaknya memberi kita beberapa waktu untuk mengedit pertanyaan untuk menjaga mereka membuka. 15 menit? Ayo sekarang.
Steven Evers
4
@DannyVarod: 1. "Tidak" bukan jawaban yang dapat diterima 2. Pertanyaan ditutup 3. Jika Anda setuju bahwa pertanyaan tidak boleh ada di sini, beri tanda / suara untuk menutupnya atau mengeditnya menjadi lebih baik .
Steven Evers
2
@ Thorbjørn Ravn Andersen - Apa yang bisa dipelajari dari tamparan? Ini tidak mengandung informasi!

Jawaban:

12

Catatan cepat tentang di mana bahasa itu harus membersihkannya: C # adalah bahasa pemrograman tujuan umum ; tidak seperti C (seperti C ++) itu berusaha untuk abstraksi tinggi; tidak seperti dialek Lisp, ia bertujuan untuk ekspresi praktis, dan, tidak seperti Java, ia lebih didorong secara agresif - Microsoft cepat menanggapi permintaan.

Itulah mengapa itu berubah menjadi campuran LINQ, lambdas dan kata kunci baru yang aneh - itu beradaptasi dengan domain masalah baru dengan cepat, dan ini memang lereng yang licin terhadap bahasa yang begitu rumit sehingga sangat sedikit yang dapat menggunakannya dengan benar (seperti C ++). Ini bukan masalah dengan C # itu sendiri, itu masalah dengan bahasa apa pun dengan tujuan ambisius ini.

Komunitas mengetahui hal ini dan, yang lebih penting, orang-orang di belakang C # sangat menyadari hal ini (beberapa entri blog dan podcast tentang apa yang ada di balik penambahan baru di C # 5.0 menunjukkan betapa buruknya orang-orang ini ingin menjaga hal-hal sederhana). Microsoft sedang mencoba untuk mengambil beberapa beban dari andalan mereka sehingga tidak menjadi tarpit: pengenalan DLR, sorotan terang atas bahasa baru (F #).

Selain itu, materi kursus tentang C # (termasuk sertifikasi MS) merekomendasikan gaya penggunaan yang berbeda (tapi konsisten) untuk C # tergantung pada masalahnya - pilih senjata Anda dan tetap gunakan: LINQ gaya Lancar pada beberapa masalah, lambdas dengan TPL pada masalah lain, polos -banyak untuk kebanyakan.

vski
sumber
2
Bisakah Anda menjelaskan apa yang Anda maksud dengan "tidak seperti dialek Lisp yang bertujuan untuk ekspresi praktis"?
Giorgio
27

Tidak. C # memberi kita lebih banyak opsi untuk menulis kode dengan lebih ringkas. Ini bisa digunakan atau disalahgunakan. Jika digunakan dengan benar, itu membuat kode lebih mudah dibaca. Jika digunakan secara tidak benar dapat membuat kode lebih sulit dibaca. Tetapi programmer yang buruk selalu memiliki keterampilan dalam menulis kode yang sulit dibaca.

Mari kita ambil dua contoh, keduanya berdasarkan pada contoh yang ditemukan di StackOverflow mengenai masalah yang sama, menemukan jenis dengan atribut tertentu:

C # 2.0:

static IEnumerable<Type> GetTypesWithAttribute<TAttribute>(bool inherit) 
                              where TAttribute: System.Attribute
{
    foreach(Assembly assembly in AppDomain.Current.GetAssemblies())
    {
        foreach(Type type in assembly.GetTypes()) 
        {
            if (type.IsDefined(typeof(TAttribute),inherit))
                yield return type;
        }
    }
}

C # 4.0:

IEnumerable<Type> GetTypesWith<TAttribute>(bool inherit) 
                              where TAttribute: System.Attribute
{ 
    return from assembly in AppDomain.CurrentDomain.GetAssemblies()
           from type in assembly.GetTypes()
           where type.IsDefined(typeof(TAttribute),inherit)
           select type;
}

IMHO, sintaks baru membuatnya jauh lebih mudah dibaca.

Pete
sumber
sintaks baru memang lebih mudah dibaca, tetapi lebih mudah dipahami, seperti apa yang terjadi di bawah tenda? Contoh pertama bisa dimengerti, yang kedua lebih mudah dibaca.
nawfal
4
@nawfal Dalam pengalaman saya, orang-orang memiliki lebih banyak kesulitan memahami konstruksi "hasil pengembalian" daripada pernyataan LINQ; jadi saya berpendapat yang kedua lebih mudah dibaca dan dimengerti. Tidak ada yang benar-benar memberi tahu Anda apa yang sedang terjadi di bawah tenda, karena keduanya memetakan abstraksi jauh dari cara prosesor sebenarnya bekerja.
Chuu
Sejujurnya, saya tidak suka LINQ karena abstrak loop dan mendorong coders untuk mengambil data dalam permintaan LINQ yang terpisah (dengan loop terpisah di latar belakang) daripada satu loop dengan inti yang lebih rumit.
mg30rg
8

Ketika LINQ ditambahkan, itu mempopulerkan gaya pengkodean yang melibatkan banyak rantai metode Fluent-style, dan meneruskan lambdas sebagai parameter.

Gaya ini sangat kuat sekali Anda merasa nyaman dengan itu. Namun dapat disalahgunakan untuk membuat kode yang cukup tidak dapat dibaca. Saya tidak berpikir contoh Anda terlalu buruk, meskipun indentasi agak acak (dan itu adalah fitur umum dari gaya kode ini, Visual Studio tidak secara otomatis indentasi dengan sangat konsisten).

Di tempat kerja saya, kami membahas gaya pengkodean ini ketika meninjau standar pengkodean kami awal tahun ini, dan kami memutuskan untuk mendorong pengembang untuk memecah kode seperti itu: secara khusus, untuk tidak membuat dan menginisialisasi objek anonim di dalam panggilan fungsi. Jadi kode Anda akan menjadi:

var assemblyCatalog = AssemblySource.Instance
    .Select(x => new AssemblyCatalog(x))
    .OfType<ComposablePartCatalog>();
var aggregateCatalog = new AggregateCatalog(assemblyCatalog);
container = CompositionHost.Initialize(aggregateCatalog);
Carson63000
sumber
1
Bukankah aneh membuat jenis baru, dan kemudian secara dinamis mengaktifkan jenis itu setelah itu?
DeadMG
Mungkin, saya baru saja memotong snipet kode tanpa pertimbangan apa pun maksudnya. Hanya ingin membagi semua instantiations menjadi baris kode mereka sendiri untuk menunjukkan efek apa yang akan berdampak pada tampilan kode.
Carson63000
Saya bertanggung jawab atas indentasi :(). Inilah yang asli: devlicio.us/blogs/rob_eisenberg/archive/2010/07/08/…
1
Saya sepenuhnya setuju dengan jawaban itu. Masalah dalam contoh potongan kode bukanlah sintaks C # yang baru, yang mana penulis mencoba menjadi pandai dengan kode "satu-liner". Ada aturan lama yang berasal dari UNIX: semuanya seharusnya hanya melakukan satu hal, dan melakukannya dengan baik. Itu berlaku untuk kelas, itu berlaku untuk metode dan, tentu saja, itu pasti berlaku untuk baris kode.
Laurent Bourgault-Roy
4

Kode dalam contoh Anda tidak mudah dibaca karena

  • Ini mencampur banyak gagasan (Komposisi, Katalog, Agregat, Assemblies, ComposableParts ...) dengan beberapa tingkat bersarang sementara kode panggilan biasanya harus berurusan dengan hanya satu level. Tampak seperti pelanggaran terhadap Hukum Demeter hanya dengan pemanggilan metode bersarang alih-alih rantai sub-sub-properti. Ini mengacaukan niat di belakang garis kode.

  • Ada penggunaan tunggal yang aneh - AssemblySource.Instance.Select()menyiratkan bahwa Instance adalah IEnumerable, yang agak canggung.

  • Variabel x dalam ekspresi lambda jelek. Umumnya Anda mencoba memberi variabel lambda niat Anda mengungkap nama - membantu pembaca mengidentifikasi jenis data apa fungsinya, bahkan jika dia tidak terbiasa dengan lambdas.

  • Tidak jelas mengapa Anda harus memfilter dengan OfType<T>()koleksi objek yang baru saja Anda buat ...

Namun, semua kekurangan ini berhubungan dengan cara pengembang asli menulis kode dan seberapa ekspresif dia mendesainnya. Ini bukan sesuatu yang spesifik untuk versi terbaru C # dan penampilan ekspresi lambda.

Lebih umum, ini membantu jika pembaca kode Anda tahu bagaimana lambdas bekerja, tetapi dengan penamaan yang cukup jelas Anda hampir selalu berhasil membuat kode Anda dapat dibaca bahkan oleh seseorang dengan latar belakang pra-.NET 3.5.

guillaume31
sumber
2

Kapan pun versi baru bahasa populer memperoleh konstruksi baru, perdebatan serupa muncul.

Saya juga memiliki keraguan ini bertahun-tahun yang lalu (http://gsscoder.blogspot.it/2009/08/use-csharps-features-wright.html).

Menurut pendapat saya C # berkembang dengan cara yang elegan dan konsisten.

Kode dalam sampel Anda tidak rumit, mungkin Anda tidak terbiasa dengan ekspresi lamda.

Masalahnya adalah bahwa C # tidak hanya bahasa berorientasi objek, sekarang mendukung konstruksi fungsional juga (http://archive.msdn.microsoft.com/FunctionalCSharp/).

gsscoder
sumber
1

Saya butuh sedikit waktu untuk memahami sintaks Lambda, dan saya dari latar belakang matematika dan fisika. Ini agak mirip dengan ekspresi matematika, meskipun mereka menggunakan -> bukannya =>:

Contoh matematika f (x): = x-> x + 2 ini berbunyi "Fungsi f dari x didefinisikan sebagai x peta ke x + 2

contoh c # del myDelegate = x => x +2; ini berbunyi "myDelegate adalah fungsi sedemikian rupa sehingga myDelegate (x) memetakan x ke x + 2

Ketidakkonsistenan antara matematika dan pemrograman tidak benar-benar membantu, meskipun saya kira -> telah diambil di C ++ sebagai "properti" (urrgghh!)

http://en.wikipedia.org/wiki/List_of_mathematical_symbols

Andy R
sumber
"walaupun saya kira -> telah diambil dalam C ++ sebagai" properti "(urrgghh!)" Seolah! Buruk dalam C ++ itu berarti "properti yang dialokasikan secara dinamis ...". Jika sesuatu tidak dinamis, Anda menggunakan periode seperti di setiap bahasa lainnya.
mg30rg