Menyimpan data dalam kode


17

Beberapa kali di masa lalu saya, saya ingin menyimpan data dalam kode. Ini akan menjadi data yang jarang berubah dan digunakan di tempat-tempat di mana akses ke database tidak mungkin, praktis, atau diinginkan. Contoh kecil akan menyimpan daftar negara. Untuk itu Anda bisa melakukan sesuatu seperti:

public class Country
{
    public string Code { get; set; }
    public string EnglishName {get;set;}
}

public static class CountryHelper
{
    public static List<Country> Countries = new List<Country>
        {
            new Country {Code = "AU", EnglishName = "Australia"},
            ...
            new Country {Code = "SE", EnglishName = "Sweden"},
            ...
        };

    public static Country GetByCode(string code)
    {
        return Countries.Single(c => c.Code == code);
    }
}

Saya lolos dengan melakukan ini di masa lalu karena set data relatif kecil dan objeknya cukup sederhana. Sekarang saya sedang mengerjakan sesuatu yang akan memiliki objek yang lebih kompleks (5 - 10 properti masing-masing, beberapa properti menjadi kamus) dan sekitar 200 objek total.

Data itu sendiri berubah sangat jarang dan ketika itu berubah itu benar-benar tidak penting. Jadi menggulungnya ke versi rilis berikutnya sangat baik.

Saya berencana untuk menggunakan T4 atau ERB atau solusi templating lainnya untuk mengubah sumber data saya menjadi sesuatu yang disimpan secara statis dalam perakitan.

Sepertinya pilihan saya

  1. Simpan data dalam XML. Kompilasi file XML sebagai sumber rakitan. Muat data sesuai kebutuhan, simpan data yang dimuat ke dalam Kamus untuk kinerja penggunaan yang berulang.
  2. Menghasilkan beberapa jenis objek statis atau objek yang diinisialisasi saat startup.

Saya cukup yakin saya memahami implikasi kinerja opsi 1. Setidaknya, firasat saya adalah bahwa ia tidak akan memiliki kinerja bintang.

Adapun opsi 2, saya tidak tahu harus berbuat apa. Saya tidak cukup tahu tentang internal. NET framework untuk mengetahui cara terbaik untuk benar-benar menyimpan data ini dalam kode C # dan cara terbaik untuk menginisialisasi itu. Saya mencari-cari menggunakan reflektor .NET untuk melihat cara System.Globalization.CultureInfo.GetCulture(name)kerjanya, karena ini sebenarnya alur kerja yang sangat mirip dengan apa yang saya inginkan. Sayangnya jejak itu berakhir pada extern, jadi tidak ada petunjuk di sana. Apakah menginisialisasi properti statis dengan semua data, seperti dalam contoh saya, cara untuk pergi? Atau akan lebih baik untuk membuat objek sesuai permintaan dan kemudian cache, seperti ini?

    private static readonly Dictionary<string, Country> Cache = new Dictionary<string,Country>(); 

    public static Country GetByCode(string code)
    {
        if (!Cache.ContainsKey(code))
            return Cache[code];

        return (Cache[code] = CreateCountry(code));
    }

    internal static Country CreateCountry(string code)
    {
        if (code == "AU")
            return new Country {Code = "AU", EnglishName = "Australia"};
        ...
        if (code == "SE")
            return new Country {Code = "SE", EnglishName = "Sweden"};
        ...
        throw new CountryNotFoundException();
    }

Keuntungan membuat mereka sekaligus di anggota statis adalah Anda dapat menggunakan LINQ atau apa pun untuk melihat semua objek dan meminta mereka jika Anda mau. Meskipun saya curiga melakukan ini memiliki penalti kinerja startup. Saya berharap seseorang memiliki pengalaman dengan ini dan dapat membagikan pendapat mereka!


5
200 benda? Saya tidak berpikir Anda harus khawatir tentang kinerja, terutama jika itu adalah biaya satu kali.
svick

1
Anda memiliki opsi lain, yaitu menyimpan objek dalam kode, lalu meneruskan referensi ke injeksi dependensi seperti biasa. Dengan begitu, jika Anda ingin mengubah cara mereka dibangun, Anda tidak memiliki referensi statis yang menunjuk langsung ke mereka dari mana-mana.
Amy Blankenship

@vick: Itu benar. Saya mungkin terlalu berhati-hati tentang kinerja.
mroach

@AmyBlankenship Saya selalu menggunakan metode pembantu, tapi DI adalah ide yang bagus. Saya belum memikirkannya. Saya akan mencobanya dan melihat apakah saya suka polanya. Terima kasih!
mroach

" Data itu sendiri berubah sangat jarang dan ketika itu berubah itu benar-benar bahkan tidak penting. " Katakan itu ke Bosnia, Serbia, Kroasia, Ukrania, Sudan Selatan, mantan Yaman Selatan, mantan Soviet, dkk. Katakan itu kepada setiap programmer yang harus berurusan dengan transisi ke mata uang Euro, atau kepada programmer yang mungkin harus berurusan dengan potensi keluar Yunani dari itu. Data termasuk dalam struktur data. File XML berfungsi dengan baik, dan dapat dengan mudah diubah. Basis data Ditto SQL.
Ross Patterson

Jawaban:


11

Saya akan pergi dengan opsi satu. Sederhana & mudah dibaca. Orang lain yang melihat kode Anda akan langsung memahaminya. Juga akan lebih mudah untuk memperbarui data XML Anda jika perlu. (Plus Anda dapat membiarkan pengguna memperbaruinya jika Anda memiliki front end yang bagus dan menyimpan file secara terpisah)

Hanya optimalkan jika Anda perlu - optimasi prematur itu jahat :)


3
Jika Anda menulis C # maka Anda menjalankan pada platform yang dapat mem-parsing XML kecil pencahayaan dengan cepat. Jadi tidak ada masalah dengan masalah kinerja.
James Anderson

7

Mengingat bahwa ini pada dasarnya pasangan kunci-nilai, saya sarankan menyimpan string ini sebagai file sumber daya yang tertanam dalam perakitan Anda pada waktu kompilasi. Anda kemudian dapat membacanya menggunakan ResourceManger. Kode Anda akan terlihat seperti ini:

private static class CountryHelper
{
    private static ResourceManager rm;

    static CountryHelper()
    {
        rm = new ResourceManager("Countries",  typeof(CountryHelper).Assembly);
    }

    public static Country GetByCode(string code)
    {
        string countryName = rm.GetString(code + ".EnglishName");
        return new Country { Code = code, EnglishName = countryName };
    }
}

Untuk membuatnya sedikit lebih efisien, Anda dapat memuat semuanya sekaligus, seperti ini:

private static class CountryHelper
{
    private static Dictionary<string, Country> countries;

    static CountryHelper()
    {
        ResourceManager rm = new ResourceManager("Countries",  typeof(CountryHelper).Assembly);
        string[] codes = rm.GetString("AllCodes").Split("|"); // AU|SE|... 
        countries = countryCodes.ToDictionary(c => c, c => CreateCountry(rm, c));
    }

    public static Country GetByCode(string code)
    {
        return countries[code];
    }

    private static Country CreateCountry(ResourceManager rm, string code)
    {
        string countryName = rm.GetString(code + ".EnglishName");
        return new Country { Code = "SE", EnglishName = countryName };
    }
}

Ini akan membantu Anda keluar banyak jika Anda merasa perlu untuk membuat aplikasi dukungan beberapa bahasa Anda.

Jika ini merupakan aplikasi WPF, kamus sumber daya adalah solusi yang jauh lebih jelas.


2

Keputusannya benar-benar: Apakah Anda hanya menginginkan pengembang (atau siapa pun yang memiliki akses ke sistem build) untuk memodifikasi data ketika perlu diubah, atau haruskah pengguna akhir atau mungkin seseorang dalam organisasi pengguna akhir dapat memodifikasi data?

Dalam kasus terakhir, saya menyarankan agar file dalam format yang dapat dibaca pengguna dan yang dapat diedit pengguna akan disimpan di tempat di mana pengguna dapat mengaksesnya. Jelas ketika Anda membaca data Anda harus berhati-hati; apa pun yang Anda temukan tidak dapat dipercaya sebagai data yang valid. Saya mungkin lebih suka JSON ke XML; Saya merasa lebih mudah untuk mengerti dan mendapatkan yang benar. Anda mungkin memeriksa apakah ada editor yang tersedia untuk format data; misalnya jika Anda menggunakan plist pada MacOS X maka setiap pengguna yang seharusnya mendekati data akan memiliki editor yang layak di komputernya.

Dalam kasus pertama, jika data merupakan bagian dari build Anda, itu tidak benar-benar membuat perbedaan. Lakukan apa yang paling nyaman bagi Anda. Dalam C ++, menulis data dengan overhead minimal adalah salah satu dari sedikit tempat di mana makro diizinkan. Jika pekerjaan menjaga data dilakukan oleh pihak ketiga, Anda dapat mengirim mereka file sumber, membiarkannya mengeditnya, dan Anda kemudian bertanggung jawab untuk memeriksanya dan menggunakannya dalam proyek Anda.


1

Contoh terbaik dari ini yang mungkin ada adalah WPF ... Formulir Anda ditulis dalam XAML, yang pada dasarnya adalah XML, kecuali bahwa .NET mampu rehydrate menjadi representasi objek dengan sangat cepat. File XAML dikompilasi menjadi file BAML, dan diperas ke dalam file ".resources", yang kemudian disematkan di dalam rakitan (versi terbaru. NET reflector dapat menunjukkan kepada Anda file-file ini).

Meskipun tidak ada dokumentasi yang bagus untuk mendukungnya, MSBuild sebenarnya bisa mengambil file XAML, mengonversinya menjadi BAML, dan menanamkannya untuk Anda (apakah itu untuk formulir kan?).

Jadi, pendekatan saya adalah: Simpan data Anda di XAML (itu hanya representasi XML dari grafik objek), hal-hal yang XAML ke file sumber daya, dan sematkan di dalam perakitan. Saat runtime ambil XAML, dan gunakan XamlServices untuk rehydrate. Kemudian bungkus dengan anggota statis, atau gunakan Injeksi Ketergantungan jika Anda ingin lebih dapat diuji, untuk mengkonsumsinya dari sisa kode Anda.

Beberapa tautan yang merupakan bahan referensi yang berguna:

Sebagai catatan, Anda sebenarnya dapat menggunakan file app.config, atau web.config untuk memuat objek yang cukup rumit melalui mekanisme pengaturan jika Anda ingin data diubah lebih mudah. Dan Anda juga dapat memuat XAML dari sistem file.


1

Saya kira System.Globalization.CultureInfo.GetCulture (nama) akan memanggil Windows API untuk mendapatkan data dari file sistem.

Untuk memuat data dalam kode, seperti yang dikatakan @svick, jika Anda akan memuat 200 objek, itu tidak akan menjadi masalah dalam banyak kasus, kecuali jika kode Anda berjalan pada beberapa perangkat dengan memori rendah.

Jika data Anda tidak pernah berubah, pengalaman saya hanya menggunakan daftar / kamus seperti:

private static readonly Dictionary<string, Country> _countries = new Dictionary<string,Country>();

Tetapi masalahnya adalah Anda perlu menemukan cara untuk menginisialisasi kamus Anda. Saya kira ini adalah titik sakitnya.

Jadi masalahnya diubah menjadi "Bagaimana cara menghasilkan data Anda?". Tetapi ini berhubungan dengan apa yang Anda butuhkan.

Jika Anda ingin menghasilkan data untuk negara, Anda dapat menggunakan

System.Globalization.CultureInfo.GetCultures()

dapatkan array CultrueInfo, dan Anda dapat menginisialisasi kamus Anda dengan kebutuhan Anda.

Dan tentu saja Anda bisa meletakkan kode di kelas statis, dan menginisialisasi kamus di konstruktor statis seperti:

public static class CountryHelper
{
    private static readonly Dictionary<string, Country> _countries;
    static CountryHelper()
    {
        _countries = new Dictionary<string,Country>();
        // initialization code for your dictionary
        System.Globalization.CultureInfo[] cts = System.Globalization.CultureInfo.GetCultures(System.Globalization.CultureTypes.AllCultures);
        for(int i=0; i < cts.Length; i++)
        {
            _countries.Add(cts[i].Name, new Country(cts[i]));
        }
    }

    public static Country GetCountry(string code)
    {
        Country ct = null;
        if(this._countries.TryGet(code, out ct))
        {
            return ct;
        } else
        {
            Log.WriteDebug("Cannot find country with code '{0}' in the list.", code);
            return null;
        }
    }
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.