"Tulis kode dengan cara baru (tm)"





Saya tidak suka C #, tapi saya suka mengumpulkan semua pola dan semua gula yang mereka tawarkan dari versi ke versi.



Hari ketiga saya menonton pidato Bill Wagner di Konferensi NDC , di mana dia menunjukkan bahwa Anda perlu menulis kode dengan cara baru (TM).



Dia menunjukkan banyak contoh refactoring yang baik, kodenya menjadi lebih mudah dibaca, tetapi sejak saat itu saya menyadari bahwa bahasa tersebut membutuhkan arsitek yang waras.



Gula tidak akan membantu



Ambil kode yang ditulis dengan buruk yang ditulis oleh seorang amatir di atas lututnya. Metode ini memeriksa status instance kelas dan mengembalikan nilai true jika ok dan false jika tidak.



internal bool GetAvailability()
{
    if (_runspace.RunspacePoolAvailability == RunspacePoolAvailability.Available) { return true;}
    if (_runspace.RunspacePoolAvailability == RunspacePoolAvailability.Busy) { return true;}
    return false;
}
      
      





Pemrogram mencoba, bahkan tidak satu pun dalam metode ini. Tapi kami berpengalaman, mari kita refactornya, hapus ifs dan ubah menjadi ternark:



internal bool GetAvailability()
{
    return _runspace.RunspacePoolAvailability == RunspacePoolAvailability.Available ||
           _runspace.RunspacePoolAvailability == RunspacePoolAvailability.Busy;
}
      
      





Ini menjadi jauh lebih baik, 2 baris kode daripada 5, tetapi ternark dapat diubah menjadi pola:



internal bool GetAvailability()
{
    return _runspace.RunspacePoolAvailability is RunspacePoolAvailability.Available or RunspacePoolAvailability.Busy;
}
      
      





Secara total, kami telah meninggalkan satu baris kode yang indah. Segala sesuatu! Refactoring selesai! (tidak)



internal void Invoke()
{
	if (!GetAvailability()) return;
        PowerShell _powershell = PowerShell.Create();
        _powershell.RunspacePool = _runspace;
        _powershell.Invoke()
    
}
      
      





Memanggil _powershell'a dalam _runspace'e yang tidak dapat diakses akan memunculkan pengecualian.



Semua implementasi sama buruknya karena menyelesaikan satu masalah - ia melindungi kode di belakangnya sehingga tidak mengeluarkan pengecualian, dan tampilannya sudah menjadi hal kesepuluh.



Kode itu tidak up to date, tapi artinya tidak berubah.



Lebih banyak pengecualian!



Ketika sebuah program menemui kenyataan, tentu saja, muncul situasi luar biasa. File tidak ditemukan, file dengan format salah, konten salah, tidak ada konten, Streamreader membaca null atau meneruskannya ke string kosong. Dua baris kode tertulis dan keduanya terputus, tetapi saya memperhatikan ceramah itu dan melihat saya.



β€œTapi khawatir, sekarang, saat membuat kelas atau perpustakaan Anda sendiri, Anda tidak perlu memikirkan tentang kode pertahanan, kompilator melakukan pemeriksaan ketik untuk kami, dan memeriksa nol tidak pernah semudah ini!



Lemparkan saja semuanya ke pengguna perpustakaan dan tulis kodenya. Melempar pengecualian dan membuat program kini menjadi bergengsi! Saya melempar, dan Anda menangkap! "


Bahwa, seperti yang saya pahami dalam ceramah Bill Wagner - NDC Conferences 2020,



saya sangat terinspirasi oleh konsep ini, dan karya .net secara umum, jadi saya akan menceritakan kisah nyata pengembangan kelas RunspacePool dan Powershell dari System .Management.Automation, yang baru-baru ini saya temui:



Perokok # 1 membuat Powershell



Pertama-tama, tentu saja, untuk melacak status, kami membuat bidang boolean yang berubah menjadi benar saat metode Buang dipanggil.



Biasanya tidak aman untuk menampilkan kolom IsDisposed, karena jika CLR mengumpulkan sampah, Anda dapat menangkap referensi Null.



class PowershellInNutshell() : IDisposable
{
    private static bool IsDisposed = false;
    private static RunspacePoolInTheNuttshell;

    public static void Invoke()
    {
        if (IsDisposed) throw new ObjectDisposedException();
        Console.WriteLine("I was invoked");
    }

    public void Dispose()
    {
        if (IsDisposed) throw new ObjectDisposedException("Invoke","   ,      ,  ");
        IsDisposed = true;
        Console.WriteLine("I was invoked");
        GC.SuppressFinalize(this);
    }
}
      
      





Ketika kita memanggil Buang atau metode lain lagi, kita melempar pengecualian dan membiarkan pemrogram lain juga memantau status instance atau menangkap pengecualian dengan kodenya, tetapi ini bukan masalah saya.



Perokok # 2 membuat RunspacePooll



Di sini kami juga membuat kolom IsDisposed, tetapi kali ini kami membuatnya dengan getter publik, sehingga orang yang menggunakan library tidak perlu menulis kode keamanan lagi.



class RunspacePoolInTheNuttshell() : IDisposable
{
    public static bool IsDisposed = false;
    
    public void Dispose()
    {
        if (IsDisposed) return;
        IsDisposed = true;
        GC.SuppressFinalize(this);
        Console.WriteLine("I was invoked");
    }
}
      
      





Jika Buang telah dipanggil, kembali dan selesai. Tentu saja, ketika mengakses kembali lapangan, itu akan menerima nullref, karena objek tersebut sudah akan dihapus dari memori, tetapi apakah ini masalah saya.



Orang yang sehat menggunakan perpustakaan:



Ini pengecualian, tidak ada pengecualian, di sini kami membungkus ikan. Kedua kelas tersebut hadir dalam paket yang sama dan memiliki perilaku yang berbeda. Kelas melempar jenis pengecualian yang sama untuk alasan yang berbeda.



  • Kata sandi salah? InvalidRunspacePoolStateException!
  • Tidak ada koneksi? InvalidRunspacePoolStateException!


Ternyata di satu tempat Anda perlu menangani ObjectDisposedException, di NullReferenceException lain di InvalidRunspacePoolStateException ketiga, dan semuanya penuh kejutan.



Pengecualian bukanlah solusi





Sebelum komuni tata cara suci, saya membaca file dengan cara lama:



public static void Main()
{
    string txt = @"c:\temp\test.txt";
    
    if (File.Exists(txt)) return;
    string readText = File.ReadAllText(txt);
    Console.WriteLine(readText);
}
      
      





Namun setelah menonton video tersebut, saya mulai melakukan dengan cara baru:



public static void Main()
{
    string txt = @"c:\temp\test.txt";
    try
    {
        string readText = File.ReadAllText(txt);
        Console.WriteLine(readText);
    }
    catch (System.IO.FileNotFoundException)
    {
        Console.WriteLine("File was not found");
    }
}
      
      





Atau cara baru?



public static void Main()
{
    string txt = @"c:\temp\test.txt";

    if (!File.Exists(txt))
    {
        throw new NullReferenceException();
    }
    string readText = File.ReadAllText(txt);
    Console.WriteLine(readText);
}
      
      





Bagaimana tepatnya itu dengan cara baru? Di mana tanggung jawab pengembang berakhir dan tanggung jawab pengguna dimulai?





Secara umum, idenya jelas, jika Anda adalah penulis perpustakaan, maka Anda dapat segera memanggil metode dan mengirimkan data yang salah ke sana, Anda tahu bidang subjek dengan sempurna dan menjelaskan semua kasus perilaku yang salah, membuang pengecualian yang membuat merasakan dan menanganinya sendiri.



internal class NewWay
{
    public static string _a;
    public static string _b;
    public static string _c;

    public static void NewWay(string a, string b, string c)
    {
        string _a = a ?? throw new NullReferenceException("a is null");
        string _b = b ?? throw new NullReferenceException("b is null");
        string _c = c ?? throw new NullReferenceException("c is null");
    }
    public void Print()
    {
        if (String.Compare(_a, _b) != 0)
        {
            throw new DataException("Some Other Ex");
        }

        Console.WriteLine($"{_a + _b + _c}");// 
    }
}
      
      





try
{
    NewWay newway = new(stringThatCanBeNull, stringThatCanBeNull, stringThatCanBeNull);
    newway.Print();
}
catch (NullReferenceException ex)
{
    Console.WriteLine(" ");
}
catch (DataException ex)
{
    Console.WriteLine(" ");
}

      
      





Yang paling cerdik sudah mengerti kemana saya memimpin. Organisasi koreksi kesalahan berdasarkan blok coba tangkap hanya akan mengarah pada penumpukan kode yang lebih dalam.



Menggunakan pola ini, bagaimanapun, kami menolak untuk mengeksekusi kode, tetapi lebih sopan.



Secara umum, bukan hal baru, 10 tahun yang lalu orang mulai curiga bahwa C # kelebihan beban dengan pola dan dari tahun ke tahun tidak berkurang. Temui satu sama lain, melempar pengecualian menjadi lebih mudah.



Dan akhirnya - para operator





Operator tidak boleh mentransmisikan apa pun di mana pun.



Contoh dari JS yang mungkin Anda ketahui:



console.log('2'+'2'-'2');
// 20
      
      



 

Desainer JS menganggap bahwa operator penambahan terpisah dan operator penggabungan terpisah tidak diperlukan, jadi melakukan matematika di JS tidak aman.



Sumber bug ini di JS adalah konversi implisit dari tipe string ke tipe int menggunakan operator. Begitulah kelebihan gula menjadi bug.



C # juga menderita pengecoran tipe implisit, meskipun lebih jarang. Ambil contoh, input pengguna, yang, setelah memperbarui pustaka, mulai dipetakan ke string alih-alih int, seperti sebelumnya, dan operator (+) dan operator matematika dan operator penggabungan. 



Mengubah tipe dari int menjadi string tidak merusak kode, tetapi merusak logika bisnis.



Jadi saya akan tinggalkan di sini, dan Anda mencoba menebak hasil eksekusi tanpa berjalan. 



class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine($"{'2' + '2' - '2' }");
    }
}
      
      








All Articles