it-swarm-id.com

Apakah penggunaan LINQ dan Lambda Expressions menyebabkan kode yang kurang dapat dibaca?

Saya sedang berdiskusi dengan rekan kerja di Linq, saya akan salin di sini:

Rekan kerja: Mari jujur ​​di sini. Sintaks Linq menyebalkan. Ini membingungkan dan tidak intuitif.

Saya: oh ayolah, lebih membingungkan daripada T-SQL?

Rekan kerja: eh, ya.

Saya: ini memiliki bagian dasar yang sama, pilih, di mana, dan dari

Co-Worker: Linq, bagi saya, adalah bastardisasi relasional + OO. Rekan kerja: Jangan salah paham - ini sangat kuat, tetapi mereka menggunakan kembali SQL untuk menggunakan koleksi objek yang berlawanan.

Saya berpendapat bahwa menggunakan Linq + Lamda sangat kuat (dia setuju), dan juga membuat kode lebih mudah dibaca (dia tidak setuju tentang hal itu):

pickFiles = from f in pickFolder.GetFiles("*.txt")
where ValidAuditFileName.IsMatch(f.Name)
select f;

atau

var existing = from s in ActiveRecordLinq.AsQueryable<ScannedEntity>()
where s.FileName == f.FullName && s.DocumentType != "Unknown"
select s;

atau (kode VB di sini)

   Dim notVerified = From image In images.AsParallel
     Group Join verifyFile In verifyFolder.GetFiles("*.vfy").AsParallel.Where(
      Function(v) v.Length > 0
      ).AsParallel
   On image.Name.Replace(image.Extension, ".vfy") Equals verifyFile.Name
     Into verifyList = Group
    From verify In verifyList.DefaultIfEmpty
    Where verify Is Nothing
    Select verify

Bagi saya ini bersih dan mudah (setidaknya lebih mudah daripada alternatif) untuk dibaca, apa pendapat Anda tentang itu?

43
BlackICE

Saya tidak dapat menemukan pos yang tepat lagi, tetapi Eric Lippert (dan mungkin beberapa softies lainnya) telah beberapa kali berpendapat tentang bagaimana Linq deklaratif, yang, untuk beberapa kelas masalah, jauh lebih intuitif dari imperatif sintaks.

Linq memungkinkan Anda menulis kode yang mengekspresikan maksud , bukan mekanisme .

Anda memberi tahu saya mana yang lebih mudah dibaca. Ini:

IEnumerable<Customer> GetVipCustomers(IEnumerable<Customer> source)
{
    List<Customer> results = new List<Customer>();
    foreach (Customer c in source)
    {
        if (c.FirstName == "Aaron")
        {
            results.Add(c);
        }
    }
    results.Sort(new LastNameComparer());
    return results;
}

class LastNameComparer : IComparer<Customer>
{
    public int Compare(Customer a, Customer b)
    {
        return x.LastName.CompareTo(b.LastName);
    }
}

Atau ini?

IEnumerable<Customer> GetVipCustomers(IEnumerable<Customer> source)
{
    return from c in source
           where c.FirstName == "Aaron"
           orderby c.LastName
           select c;
}

Atau bahkan ini?

IEnumerable<Customer> GetVipCustomers(IEnumerable<Customer> source)
{
    return source.Where(c => c.FirstName == "Aaron").OrderBy(c => c.LastName);
}

Contoh pertama hanyalah sekelompok boilerplate sia-sia untuk mendapatkan hasil yang paling sederhana. Siapa pun yang berpikir bahwa itu lebih banyak dapat dibaca daripada versi Linq perlu diperiksa kepalanya. Bukan hanya itu, tetapi yang pertama membuang-buang memori. Anda bahkan tidak dapat menulisnya menggunakan yield return karena penyortiran.

Rekan kerja Anda dapat mengatakan apa yang diinginkannya; secara pribadi, saya pikir Linq telah meningkatkan keterbacaan kode saya tak terkira.

Tidak ada yang "relasional" tentang Linq juga. Ini mungkin memiliki beberapa kesamaan dangkal dengan SQL tetapi tidak mencoba dalam bentuk apa pun untuk menerapkan kalkulus relasional. Hanya sekelompok ekstensi yang membuatnya lebih mudah untuk melakukan kueri dan memproyeksikan urutan. "Kueri" tidak berarti "relasional", dan sebenarnya ada beberapa basis data non-relasional yang menggunakan sintaks seperti SQL. Linq adalah murni berorientasi objek, kebetulan bekerja dengan database relasional melalui kerangka kerja seperti Linq ke SQL karena beberapa voodoo pohon ekspresi dan desain pintar dari tim C #, membuat fungsi anonim secara implisit dapat dikonversi menjadi pohon ekspresi.

74
Aaronaught

Rekan kerja: Mari jujur ​​di sini. Sintaks Linq menyebalkan. Ini membingungkan dan tidak intuitif.

Anda tidak bisa berdebat dengan kritik itu. Untuk rekan kerja Anda, itu menyebalkan . Kami gagal merancang sintaksis yang, bagi mereka, jelas dan intuitif. Itu gagal kami, dan Anda bisa menyampaikan permintaan maaf saya kepada rekan kerja Anda. Saya senang menerima saran tentang cara menjadikannya lebih baik; apa yang menurut rekan kerja Anda membingungkan atau tidak intuitif?

Namun, Anda tidak bisa menyenangkan semua orang. Pendapat pribadi saya, dan pendapat sebagian besar orang yang saya ajak bicara tentang masalah ini, adalah bahwa sintaks pemahaman query jauh lebih jelas daripada sintaksis imperatif setara. Jelas tidak semua orang setuju, tetapi untungnya kami tidak memerlukan konsensus dari jutaan pelanggan kami ketika kami merancang bahasa.

Mengenai masalah apa yang "intuitif", saya diingatkan akan kisah ahli bahasa Inggris yang mempelajari banyak bahasa berbeda dan akhirnya menyimpulkan bahwa bahasa Inggris adalah yang terbaik dari semua bahasa karena dalam bahasa Inggris, kata-katanya datang dalam urutan yang sama seperti yang Anda pikirkan. Tidak seperti bahasa Prancis, di mana mereka terus-menerus mengatakan hal-hal seperti "anjing putih memakan daging merah". Betapa sulitnya bagi orang Prancis untuk berpikir kata-kata dalam benar memesan dan kemudian harus mengatakan dalam urutan Prancis ! Bahasa Prancis sangat tidak intuitif! Sungguh menakjubkan bahwa Prancis berhasil berbicara itu. Dan Jerman? di mana mereka berpikir "anjing makan daging" tetapi kemudian harus mengatakan "anjing makan daging"!?! Sangat tidak intuitif.

Seringkali apa yang "intuitif" hanyalah masalah keakraban. Butuh waktu bulan untuk bekerja dengan LINQ sebelum saya berhenti memulai pertanyaan saya dengan klausa "pilih". Sekarang sudah sifat kedua, dan urutan SQL tampaknya aneh.

Yang mana itu! Aturan pelingkupan semua kacau dalam SQL. Sesuatu yang Anda mungkin ingin tunjukkan kepada rekan kerja Anda adalah bahwa LINQ dirancang dengan hati-hati sehingga (1) pengenalan variabel dan cakupan terjadi dari kiri ke kanan (*), dan (2) urutan kueri muncul pada halaman tersebut. urutan pelaksanaannya. Itulah saat Anda mengatakannya

from c in customers where c.City == "London" select c.Name

c muncul di lingkup di sebelah kiri, dan tetap di lingkup di sebelah kanan. Dan urutan terjadinya adalah: "pelanggan" pertama dievaluasi. Kemudian "di mana" dievaluasi untuk menyaring urutan. Kemudian urutan yang difilter diproyeksikan oleh "pilih".

SQL tidak memiliki properti ini. Jika Anda mengatakan

SELECT Name FROM Customers WHERE City = 'London'

kemudian "Nama" dibawa ke ruang lingkup oleh sesuatu ke kanan, bukan ke kiri, dan kueri dieksekusi dalam urutan yang benar-benar kacau; klausa tengah dievaluasi terlebih dahulu, kemudian klausa terakhir, dan kemudian klausa pertama. Itu sekarang tampak gila dan tidak intuitif bagi saya, telah bekerja hanya dengan LINQ begitu lama sekarang.


(*) Aturan pelingkupan agak aneh di LINQ dengan klausa gabungan. Tapi selain itu, cakupan sarangnya bagus.

69
Eric Lippert

Seperti hal lain di dunia pemrograman, Anda harus terbiasa dengan sintaks, dan kemudian (berpotensi) lebih mudah dibaca.

Seperti hal lain di dunia pemrograman, ada potensi kode spaghetti atau pelanggaran lainnya.

Seperti hal lain di dunia pemrograman, Anda bisa melakukannya dengan cara ini atau cara lain.

Seperti hal lain di dunia pemrograman, jarak tempuh Anda mungkin beragam.

22
Wonko the Sane

Saya melihat komentar/komentar di mana ia menyatakan sesuatu - dalam kaitannya dengan LINQ/lambda - di sepanjang baris: "Tulis kode yang dapat dibaca oleh manusia, daripada dibaca oleh komputer Anda".

Saya pikir pernyataan ini memiliki banyak manfaat, namun, pertimbangkan pengembang (seperti saya) yang telah melalui keseluruhan penuh bahasa pengembangan dari Majelis, melalui prosedural, melalui OO, melalui dikelola, dengan memanfaatkan solusi paralel tugas throughput tinggi .

Saya bangga membuat kode saya dapat dibaca dan dapat digunakan kembali sebanyak mungkin dan mengadopsi banyak prinsip pola desain GOF untuk memberikan sistem dan layanan kualitas produksi di sejumlah besar sektor bisnis yang berbeda.

Pertama kali saya menemukan ekspresi lambda, saya berpikir: "Apa-apaan itu!?!" Itu langsung kontra-intuitif untuk saya familiar (dan karena itu nyaman) sintaks deklaratif eksplisit. Yang lebih muda <5 thn dalam pekerjaan namun pria menjilatnya seperti manna dari surga!

Itu karena selama bertahun-tahun berpikir seperti komputer (dalam arti sintaksis) diterjemahkan dengan sangat mudah ke dalam sintaks pengkodean langsung (terlepas dari bahasa). Ketika Anda memiliki pola pikir komputasi selama sekitar 20+ tahun (30+ dalam kasus saya), Anda harus menghargai bahwa sintaksis awal guncangan dari ekspresi lambda dapat dengan mudah diterjemahkan menjadi ketakutan dan ketidakpercayaan.

Mungkin rekan kerja di OP berasal dari latar belakang yang sama dengan saya (yaitu beberapa kali ada di sekitar blok) dan itu kontra-intuitif bagi mereka pada waktu itu? Pertanyaan saya adalah: apa yang Anda lakukan? Apakah Anda mencoba mendidik ulang teman Anda untuk memahami manfaat sintaks sebaris, atau apakah Anda menjiplak/mengucilkan mereka karena tidak "mengikuti program"? Yang pertama mungkin akan melihat rekan kerja Anda mendekati garis pemikiran Anda, yang terakhir mungkin akan membuat mereka lebih tidak mempercayai sintaksis LINQ/lambda dan dengan demikian memperburuk opini negatif.

Bagi saya sendiri, saya harus mendidik kembali cara berpikir saya sendiri (seperti yang Eric katakan di atas, itu bukan perubahan pikiran yang tidak signifikan, dan saya harus memprogram di Miranda di tahun 80-an sehingga saya memiliki pengalaman pemrograman fungsional yang saya miliki) tapi begitu saya telah melalui rasa sakit itu manfaatnya jelas tetapi - lebih penting - di mana penggunaannya lebih digunakan (yaitu digunakan untuk menggunakannya ), lebih kompleks dan berulang (mempertimbangkan prinsip DRY dalam contoh itu).

Sebagai seseorang yang tidak hanya masih menulis banyak kode tetapi juga harus secara teknis meninjau banyak kode, saya harus memahami prinsip-prinsip ini sehingga saya dapat meninjau item secara tidak memihak, menyarankan di mana penggunaan ekspresi lambda mungkin lebih efisien/dapat dibaca, dan juga untuk membuat pengembang mempertimbangkan keterbacaan ekspresi lambda inline yang sangat kompleks (di mana pemanggilan metode akan - dalam kasus tersebut - membuat kode lebih mudah dibaca, dipelihara, dan diperluas).

Jadi, ketika seseorang mengatakan bahwa mereka "Jangan mendapatkan lambda?" atau sintaks LINQ, alih-alih mencapnya luddite coba bantu mereka memahami prinsip-prinsip yang mendasarinya. Mereka mungkin memiliki latar belakang "sekolah tua" seperti saya.

6
CWC

Ekspresi Lambda mengarah ke kode yang kurang dapat dibaca jika kueri terlalu panjang. Namun itu jauh lebih baik daripada terlalu banyak loop bersarang.

Lebih baik dengan campuran keduanya.

Tulis di Lambda jika lebih cepat (Anda harus cepat) atau lebih mudah dibaca.

4
Amir Rezaei

Saya pikir itu tergantung pada kebanyakan kasus (kecuali ketika melakukan sesuatu yang sangat aneh) pada jika Anda berarti "dapat dibaca" sebagai seseorang mendapatkan ide tentang apa yang sedang terjadi atau jika mereka dapat dengan mudah menemukan semua detail kecil kecil.

Saya pikir tautan membantu dengan yang pertama, tetapi sering (terutama ketika debugging) menyakiti yang terakhir.

IMHO ketika saya melihat kode saya tidak terbiasa dengan yang pertama jauh lebih penting daripada yang terakhir, jadi saya merasa jauh lebih mudah dibaca.

1
Bill

Saya menemukan sintaksis LINQ intuitif dan mudah dibaca, terutama karena mereka meletakkan FROM di awal di mana itu bukan di tengah seperti di SQL. Tapi lambda IMO membingungkan dan membuat kode lebih sulit dibaca.

1
Mason Wheeler

Saya setuju dengan Anda bahwa sintaks Linq tidak jauh berbeda dari T-SQL. Saya pikir rekan kerja Anda mungkin benar-benar keberatan dengan hal-hal relasional yang tercampur di mana kode-nya Bagus dan mengkilap OO. Di sisi lain, pemrograman fungsional membutuhkan sedikit waktu untuk membiasakan diri, dan kemauan untuk terbiasalah.

1
Larry Coleman

Tergantung. Jelas, T-SQL secara unik menyediakan beberapa solusi DB-relasional. Jelas LINQ secara unik menyediakan beberapa solusi OO.

Namun; "lebih membingungkan daripada T-SQL?" - dibahas/ditanyakan dalam pertanyaan awal. Ini jelas menyiratkan beberapa fitur yang tidak satupun dari alamat jawaban yang ada, alih-alih menuduh kritikus (jelas berkenalan SQL) terjebak di masa lalu.

Jadi, meskipun saya menghargai LINQ untuk kualitas tertentu, dan tidak sangat tidak setuju dengan jawaban yang ada di sini, saya merasa tandingannya layak diwakili:

Bertahun-tahun setelah menjadi akrab dengan LINQ, melakukan beberapa jenis operasi grup, sambungan luar dan non-equijoins, bekerja dengan kunci komposit, dan operasi lain di LINQ masih membuat saya merasa ngeri. (Terutama ketika menargetkan back-end relasional dengan pertanyaan yang sensitif terhadap kinerja.)

from c in categories
join p in products on c equals p.Category into ps
from p in ps.DefaultIfEmpty()

Jika Anda berpikir itu intuitif, lebih banyak kekuatan untuk Anda. ;)

0
shannon