코드에 데이터 저장


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)실제로 .NET 리플렉터를 사용하여 작동 방식 을 확인했습니다. 실제로 내가 원하는 것과 매우 유사한 워크 플로우이기 때문입니다. 불행히도 그 흔적은에서 끝났 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 개의 물건? 특히 일회성 비용 인 경우 성능에 대해 걱정할 필요가 없다고 생각합니다.
svick

1
객체를 코드에 저장 한 다음 통과 종속성 주입에 대한 참조를 정상적으로 전달하는 다른 옵션이 있습니다. 이렇게하면 구성 방식을 변경하려는 경우 어디에서나 직접 참조하는 정적 참조가 없습니다.
Amy Blankenship

@ svick : 사실입니다. 아마도 성능에 대해 지나치게 신중한 것 같습니다.
mroach

@AmyBlankenship 항상 도우미 메서드를 사용하고 있었지만 DI가 좋습니다. 나는 그것을 생각하지 않았다. 나는 그것을 시도하고 내가 패턴을 좋아하는지 볼 것이다. 감사!
mroach

" 데이터 자체는 매우 거의 변경되지 않는 그것은 변화를 수행 할 때 그것은 심지어 중요한 것이 정말. "는 보스니아, 세르비아, 크로아티아, 우크라이나 인, 사우스 Sudanis, 전 사우스 예멘, 전 소련에 그에게 등. 유로 통화로의 전환을 처리해야하는 모든 프로그래머 또는 그리스의 잠재적 인 출구를 처리해야하는 프로그래머에게이를 설명하십시오. 데이터는 데이터 구조에 속합니다. XML 파일은 훌륭하게 작동하며 쉽게 변경할 수 있습니다. Ditto 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

실제로 결정은 다음과 같습니다. 개발자 (또는 빌드 시스템에 액세스 할 수있는 사람) 만 수정이 필요할 때 데이터를 수정하거나 최종 사용자 또는 최종 사용자 조직의 누군가가 데이터를 수정할 수 있도록 하시겠습니까? 데이터?

후자의 경우, 사용자가 읽을 수 있고 사용자가 편집 할 수있는 형식의 파일은 사용자가 액세스 할 수있는 장소에 저장 될 것을 제안합니다. 데이터를 읽을 때 분명히주의해야합니다. 당신이 찾은 것은 유효한 데이터로 신뢰할 수 없습니다. 아마도 JSON보다 XML을 선호 할 것입니다. 이해하고 이해하는 것이 더 쉽다는 것을 알게되었습니다. 데이터 형식에 사용 가능한 편집기가 있는지 확인할 수 있습니다. 예를 들어, MacOS X에서 plist를 사용하면 데이터 근처에 가야하는 모든 사용자는 자신의 컴퓨터에 알맞은 편집기를 갖게됩니다.

첫 번째 경우, 데이터가 빌드의 일부인 경우 실제로 차이가 없습니다. 가장 편리한 것을하십시오. C ++에서 최소한의 오버 헤드로 데이터를 작성하는 것은 매크로가 허용되는 몇 안되는 장소 중 하나입니다. 데이터 유지 관리 작업이 타사에서 수행 된 경우 소스 파일을 보내서 편집하게하면 프로젝트에서 데이터를 확인하고 사용할 책임이 있습니다.


1

존재하는 가장 좋은 예는 아마도 WPF입니다. .NET이 객체 표현으로 매우 빠르게 재수화할 수 있다는 점을 제외하고는 기본적으로 XML 인 XAML로 작성된 양식입니다. 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.