Penggunaan kreatif metode penyuluhan di C #

Halo, Habr!



Melanjutkan penelitian kami tentang C #, kami telah menerjemahkan artikel singkat berikut untuk Anda mengenai penggunaan asli metode penyuluhan. Kami menganjurkan agar Anda memberi perhatian khusus pada bagian terakhir tentang antarmuka, serta profil penulis.







Saya yakin siapa pun yang memiliki sedikit pengalaman C # menyadari keberadaan metode ekstensi. Ini adalah fitur bagus yang memungkinkan pengembang untuk memperluas tipe yang ada dengan metode baru.



Ini sangat berguna dalam kasus di mana Anda ingin menambahkan fungsionalitas ke jenis yang tidak Anda kontrol. Faktanya, siapa pun cepat atau lambat harus menulis perpanjangan untuk BCL hanya untuk membuat segalanya lebih mudah diakses.



Namun, seiring dengan kasus penggunaan yang relatif jelas, ada juga pola yang sangat menarik terkait langsung dengan penggunaan metode penyuluhan dan mendemonstrasikan bagaimana metode tersebut dapat digunakan dengan cara non-tradisional.



Menambahkan Metode ke Enumerasi



Enumerasi hanyalah kumpulan nilai numerik konstan, masing-masing diberi nama unik. Meskipun pencacahan di C # mewarisi dari kelas abstrak Enum, mereka tidak ditafsirkan sebagai kelas nyata. Secara khusus, batasan ini mencegah mereka memiliki metode.



Dalam beberapa kasus, akan sangat membantu untuk memprogram logika menjadi enumerasi. Misalnya, jika nilai enumerasi bisa ada di beberapa tampilan berbeda dan Anda ingin dengan mudah mengonversinya satu sama lain.



Misalnya, bayangkan tipe berikut dalam aplikasi umum yang memungkinkan Anda menyimpan file dalam berbagai format:



public enum FileFormat
{
    PlainText,
    OfficeWord,
    Markdown
}


Pencacahan ini menentukan daftar format yang didukung oleh aplikasi dan dapat digunakan di berbagai bagian aplikasi untuk memulai logika percabangan berdasarkan nilai tertentu.



Karena setiap format file dapat direpresentasikan sebagai ekstensi file, alangkah baiknya jika masing FileFormat- masing memiliki metode untuk mendapatkan informasi ini. Dengan metode penyuluhan ini dapat dilakukan, seperti ini:



public static class FileFormatExtensions
{
    public static string GetFileExtension(this FileFormat self)
    {
        if (self == FileFormat.PlainText)
            return "txt";

        if (self == FileFormat.OfficeWord)
            return "docx";

        if (self == FileFormat.Markdown)
            return "md";

        //  ,      ,
        //      
        throw new ArgumentOutOfRangeException(nameof(self));
    }
}


Yang, pada gilirannya, memungkinkan kita melakukan ini:



var format = FileFormat.Markdown;
var fileExt = format.GetFileExtension(); // "md"
var fileName = $"output.{fileExt}"; // "output.md"


Refactoring Model Classes



Ada kalanya Anda tidak ingin menambahkan metode langsung ke kelas, misalnya, jika Anda bekerja dengan model anemia .



Model anemia biasanya diwakili oleh sekumpulan properti publik yang tidak dapat diubah, hanya untuk mendapatkan. Oleh karena itu, saat menambahkan metode ke kelas model, Anda mungkin mendapatkan kesan bahwa kemurnian kode telah dilanggar, atau Anda mungkin mencurigai bahwa metode merujuk ke beberapa jenis status privat. Metode ekstensi tidak menyebabkan masalah ini, karena metode tersebut tidak memiliki akses ke anggota privat model dan pada dasarnya bukan bagian dari model.



Jadi, pertimbangkan contoh berikut dengan dua model, satu mewakili daftar judul tertutup, dan yang lainnya mewakili baris judul terpisah:



public class ClosedCaption
{
    //  
    public string Text { get; }

    //       
    public TimeSpan Offset { get; }

    //       
    public TimeSpan Duration { get; }

    public ClosedCaption(string text, TimeSpan offset, TimeSpan duration)
    {
        Text = text;
        Offset = offset;
        Duration = duration;
    }
}

public class ClosedCaptionTrack
{
    // ,    
    public string Language { get; }

    //   
    public IReadOnlyList<ClosedCaption> Captions { get; }

    public ClosedCaptionTrack(string language, IReadOnlyList<ClosedCaption> captions)
    {
        Language = language;
        Captions = captions;
    }
}


Dalam keadaan saat ini, jika kita perlu menampilkan string subtitle pada waktu tertentu, kita akan menjalankan LINQ seperti ini:



var time = TimeSpan.FromSeconds(67); // 1:07

var caption = track.Captions
    .FirstOrDefault(cc => cc.Offset <= time && cc.Offset + cc.Duration >= time);


Ini benar-benar membutuhkan beberapa jenis metode pembantu yang dapat diimplementasikan sebagai metode anggota atau metode ekstensi. Saya lebih suka opsi kedua.



public static class ClosedCaptionTrackExtensions
{
    public static ClosedCaption GetByTime(this ClosedCaptionTrack self, TimeSpan time) =>
        self.Captions.FirstOrDefault(cc => cc.Offset <= time && cc.Offset + cc.Duration >= time);
}


Dalam hal ini, metode ekstensi memungkinkan Anda mencapai hal yang sama seperti yang biasa, tetapi memberikan sejumlah bonus yang tidak jelas:



  1. Jelas bahwa metode ini hanya berfungsi dengan anggota publik kelas dan tidak mengubah status pribadinya dengan cara yang misterius.
  2. Jelas, metode ini hanya memungkinkan Anda untuk memotong sudut dan disediakan di sini hanya untuk kenyamanan.
  3. Metode ini milik kelas yang benar-benar terpisah (atau bahkan perakitan) yang tujuannya adalah untuk memisahkan data dari logika.


Secara umum, saat menggunakan pendekatan metode ekstensi, akan lebih mudah untuk menarik garis antara yang diperlukan dan yang berguna.



Membuat Antarmuka Serbaguna



Saat mendesain antarmuka, Anda selalu ingin kontrak dibuat sekecil mungkin karena membuatnya lebih mudah untuk diterapkan. Ini sangat membantu ketika antarmuka menyediakan fungsionalitas dengan cara yang paling umum, sehingga kolega Anda (atau Anda sendiri) dapat membangunnya untuk menangani kasus yang lebih spesifik.



Jika ini terdengar tidak masuk akal bagi Anda, pertimbangkan antarmuka umum yang menyimpan model ke file:



public interface IExportService
{
    FileInfo SaveToFile(Model model, string filePath);
}


Semuanya bekerja dengan baik, tetapi dalam beberapa minggu persyaratan baru mungkin tiba: kelas yang mengimplementasikan IExportServicetidak hanya harus mengekspor ke file, tetapi juga dapat menulis ke file.



Jadi, untuk memenuhi persyaratan ini, kami menambahkan metode baru ke kontrak:



public interface IExportService
{
    FileInfo SaveToFile(Model model, string filePath);

    byte[] SaveToMemory(Model model);
}


Perubahan ini baru saja merusak semua implementasi yang ada IExportService, karena sekarang semuanya perlu diperbarui untuk mendukung penulisan ke memori juga.



Tetapi, agar tidak melakukan semua ini, kami dapat merancang antarmuka sedikit berbeda dari awal:



public interface IExportService
{
    void Save(Model model, Stream output);
}


Dalam formulir ini, antarmuka memaksa Anda untuk menulis tujuan dalam bentuk yang paling umum, yaitu ini Stream. Sekarang kami tidak lagi terbatas pada file saat bekerja dan juga dapat menargetkan berbagai opsi keluaran lainnya.



Satu-satunya kelemahan dari pendekatan ini adalah bahwa operasi paling dasar tidak sesederhana yang biasa kita lakukan: sekarang kita harus menetapkan instance tertentu Stream, membungkusnya dalam pernyataan using dan meneruskannya sebagai parameter.



Untungnya, kelemahan ini benar-benar dibatalkan saat menggunakan metode ekstensi:



public static class ExportServiceExtensions
{
    public static FileInfo SaveToFile(this IExportService self, Model model, string filePath)
    {
        using (var output = File.Create(filePath))
        {
            self.Save(model, output);
            return new FileInfo(filePath);
        }
    }

    public static byte[] SaveToMemory(this IExportService self, Model model)
    {
        using (var output = new MemoryStream())
        {
            self.Save(model, output);
            return output.ToArray();
        }
    }
}


Dengan memfaktorkan ulang antarmuka asli, kami membuatnya jauh lebih serbaguna dan tidak mengorbankan kegunaan dengan menggunakan metode ekstensi.



Jadi, saya menemukan metode penyuluhan sebagai alat yang sangat berharga dalam menjaga kesederhanaan yang sederhana dan mengubah yang kompleks menjadi mungkin .



All Articles