コードにデータを保存する


17

過去に何度か、データをコードに保存したいと思っていました。これはめったに変更されないデータであり、データベースへのアクセスが不可能、実用的、または望ましくない場所で使用されます。小さな例は、国のリストを保存することです。そのためには、次のようなことができます。

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);
    }
}

データセットが比較的小さく、オブジェクトが非常に単純だったため、過去にこれをやめました。現在、私はより複雑なオブジェクト(それぞれ5〜10個のプロパティ、一部のプロパティは辞書)を持ち、合計で約200個のオブジェクトを持つものを扱っています。

データ自体はめったに変更されず、変更されても実際にはそれほど重要ではありません。そのため、次のリリースバージョンに組み込むことはまったく問題ありません。

T4またはERBまたはその他のテンプレートソリューションを使用して、データソースをアセンブリに静的に格納されるものに変換することを計画しています。

私のオプションは

  1. データをXMLで保存します。XMLファイルをアセンブリリソースとしてコンパイルします。必要に応じてデータをロードし、ロードしたデータを辞書に保存して、繰り返し使用するパフォーマンスを向上させます。
  2. 起動時に初期化される何らかの種類の静的オブジェクトを生成します。

オプション1のパフォーマンスへの影響を理解していると確信しています。少なくとも、私の予想では、優れたパフォーマンスは得られません。

オプション2については、何をすべきかわかりません。このデータをC#コードに実際に保存する最適な方法と、それを初期化する最適な方法を知るには、.NETフレームワークの内部について十分に知りません。System.Globalization.CultureInfo.GetCulture(name)これが実際に私が望むものに非常に類似したワークフローであるので、どのように機能するかを見るために。残念ながら、そのトレイルはで終了したexternため、ヒントはありません。私の例のように、すべてのデータで静的プロパティを初期化していますか?または、次のように、オンデマンドでオブジェクトを作成してからキャッシュする方が良いでしょうか?

    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();
    }

静的メンバーで一度に作成する利点は、LINQなどを使用してすべてのオブジェクトを調べ、必要に応じてクエリを実行できることです。これを行うと起動時のパフォーマンスが低下すると思われますが。誰かがこれを経験し、意見を共有できることを望んでいます!


5
200個のオブジェクト?特に1回限りのコストの場合、パフォーマンスについて心配する必要はないと思います。
svick

1
別のオプションとして、オブジェクトをコードに保存してから、通常どおりに依存性注入を通じて参照を渡します。そうすれば、それらの構築方法を変更したい場合、どこからでも直接参照する静的参照はありません。
エイミーブランケンシップ

@svick:それは本当です。私はおそらくパフォーマンスについて過度に用心しているでしょう。
-mroach

@AmyBlankenship私は常にヘルパーメソッドを使用しますが、DIは良いアイデアです。私はそれを考えていませんでした。それを試して、パターンが好きかどうかを確認します。ありがとう!
-mroach

データ自体は非常にまれにしか変化せず、実際に変化してもそれほど重要ではありません。」ボスニア人、セルビア人、クロアチア人、ウクライナ人、南スーダニス、旧南イエメン、旧ソビエトなどに伝えてください それを、ユーロ通貨への移行に対処しなければならなかったすべてのプログラマー、またはギリシャの潜在的な脱出に対処しなければならないプログラマーに伝えてください。 データはデータ構造に属します。XMLファイルはうまく機能し、簡単に変更できます。同じSQLデータベース。
ロスパターソン

回答:


11

私はオプション1を選択します。シンプルで読みやすいです。あなたのコードを見ている誰かがすぐにそれを理解するでしょう。必要に応じてXMLデータを更新するのも簡単です。(さらに、素敵なフロントエンドがあり、ファイルを個別に保存している場合、ユーザーにそれらを更新させることができます)

必要な場合にのみ最適化してください-早すぎる最適化は悪です:)


3
C#を記述している場合、小さなXMLを高速で解析できるプラットフォームで実行しています。そのため、パフォーマンスの問題について悩む価値はありません。
ジェームズアンダーソン

7

これらは本質的にキーと値のペアであるため、コンパイル時にこれらの文字列をアセンブリに埋め込まれたリソースファイルとして保存することをお勧めします。その後、を使用してそれらを読み取ることができResourceMangerます。コードは次のようになります。

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 };
    }
}

それをもう少し効率的にするために、次のようにそれらを一度にロードできます:

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 };
    }
}

これにより、アプリケーションで複数の言語をサポートする必要がある場合に役立ちます。

これがたまたまWPFアプリケーションである場合、リソースディクショナリははるかに明白なソリューションです。


2

決定は本当に:開発者(またはビルドシステムにアクセスできる人)だけが変更が必要なときにデータを変更するか、エンドユーザーまたはエンドユーザーの組織内の誰かがデータを変更できるようにする必要がありますか?データ?

後者の場合、ユーザーが読み取りおよび編集できる形式のファイルは、ユーザーがアクセスできる場所に保存することをお勧めします。明らかに、データを読むときは注意する必要があります。あなたが見つけたものはすべて、有効なデータであると信頼することはできません。XMLよりもJSONを好むでしょう。私は理解しやすく、正しい方法を見つけます。データ形式に使用できるエディターがあるかどうかを確認できます。たとえば、MacOS Xでplistを使用する場合、データに近づいてはならないすべてのユーザーは、自分のコンピューターに適切なエディターを持っています。

最初のケースでは、データがビルドの一部である場合、実際には違いはありません。あなたにとって最も便利なことをしてください。C ++では、最小限のオーバーヘッドでデータを書き込むことは、マクロが許可される数少ない場所の1つです。データの保守作業がサードパーティによって行われている場合、ソースファイルを送信し、それらに編集を任せることができます。その後、プロジェクトでそれらをチェックして使用する責任があります。


1

存在するこの最も良い例はおそらくWPFです。フォームはXAMLで記述されます。XAMLは基本的にXMLです。ただし、.NETはそれを非常に迅速にオブジェクト表現にリハイドレートできることを除きます。XAMLファイルはBAMLファイルにコンパイルされ、「。resources」ファイルに圧縮されます。その後、このファイルはアセンブリ内に埋め込まれます(最新バージョンの.NETリフレクターはこれらのファイルを表示できます)。

それをサポートする優れたドキュメントはありませんが、MSBuildは事実上、XAMLファイルを取得してBAMLに変換し、それを埋め込むことができます(フォームに対して適切ですか?)。

したがって、私のアプローチは次のとおりです。データをXAMLに格納し(オブジェクトグラフのXML表現にすぎません)、そのXAMLをリソースファイルに挿入し、アセンブリに埋め込みます。実行時にXAMLを取得し、XamlServicesを使用して水分補給します。次に、それを静的メンバーにラップするか、依存性注入を使用して、テストしやすくしたい場合は、残りのコードから使用します。

役立つ参考資料であるいくつかのリンク:

ちなみに、データをより簡単に変更したい場合は、実際にapp.configファイルまたはweb.configファイルを使用して、設定メカニズムを介してかなり複雑なオブジェクトをロードできます。また、ファイルシステムからXAMLを読み込むこともできます。


1

System.Globalization.CultureInfo.GetCulture(name)はWindows APIを呼び出してシステムファイルからデータを取得すると思います。

@svickが言ったように、データをコードでロードする場合、200個のオブジェクトをロードしようとしても、コードが低メモリデバイスで実行されていなければ、ほとんどの場合問題になりません。

データが変更されない場合、私の経験では、次のようなリスト/辞書を使用しています。

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

しかし問題は、辞書を初期化する方法を見つける必要があることです。これが問題だと思います。

したがって、問題は「データの生成方法」に変更されます。 しかし、これは必要なものに関連しています。

国のデータを生成する場合は、

System.Globalization.CultureInfo.GetCultures()

CultrueInfo配列を取得すると、必要に応じて辞書を初期化できます。

そしてもちろん、コードを静的クラスに入れて、静的コンストラクターで辞書を次のように初期化することもできます。

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;
        }
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.