C #에서 상수 사전 만들기


128

s를 s 로 상수 (런타임으로 변경하지 않음) 매핑 을 만드는 가장 효율적인 방법은 무엇입니까 ?stringint

const Dictionary 사용하려고 시도했지만 작동하지 않았습니다.

적절한 의미 로 불변 래퍼 를 구현할 수는 있지만 여전히 정확하게 보이지는 않습니다.


요청한 사람들을 위해 생성 된 클래스에서 IDataErrorInfo 를 구현 하고 있으며 columnName 조회를 설명자 배열로 만드는 방법을 찾고 있습니다.

나는 스위치가 문자열을 받아들이는 것을 알지 못했습니다 (테스트 할 때 오타! d' oh!). 그래서 내가 사용할 것입니다. 감사!


1
해결책은 여기 있습니다 : stackoverflow.com/questions/10066708/…

답변:


180

C #에서 컴파일 타임으로 생성 된 상수 사전을 만드는 것은 실제로 간단한 작업이 아닙니다. 실제로, 여기의 답변 중 어느 것도 실제로 그것을 달성하지 못합니다.

꼭 좋은 것은 아니지만 요구 사항을 충족하는 솔루션이 하나 있습니다. C # 사양에 따라 스위치 케이스 테이블은 상수 해시 점프 테이블로 컴파일됩니다. 즉, 일련의 if-else 문이 아닌 상수 사전입니다. 따라서 다음과 같은 switch-case 문을 고려하십시오.

switch (myString)
{
   case "cat": return 0;
   case "dog": return 1;
   case "elephant": return 3;
}

이것이 바로 당신이 원하는 것입니다. 그리고 그래, 나도 알아


9
어쨌든 코드를 생성하고 성능 특성이 좋으며 컴파일 타임을 계산할 수 있으며 사용 사례에 대한 다른 멋진 속성도 있습니다. 이렇게 할게요!
David Schmitt

4
이와 같이 값이 아닌 유형을 반환하면 클래스 불변성이 손상된다는 점에 유의하십시오.
Tom Anderson

35
스위치 케이스는 값을 "가져올"때까지 훌륭합니다.
Alex

6
사전에 가지고있는 멋진 기능이 많이 없습니다.
Zinan Xing 2019

1
@TomAnderson : 반환 된 항목이 값 유형 (변경 가능 여부)이거나 변경 불가능한 클래스 객체 인 경우 구성은 변경 불가능한 것으로 작동합니다.
supercat

37

현재 프레임 워크에는 귀중한 소수의 컬렉션이 있습니다. .NET 3.5에서 비교적 고통스럽지 않은 옵션을 생각할 수 있습니다.

사용 Enumerable.ToLookup()- Lookup<,>클래스는 불변입니다 (그러나 rhs에 대한 다중 값). 당신은 이것을 Dictionary<,>아주 쉽게 할 수 있습니다 :

    Dictionary<string, int> ids = new Dictionary<string, int> {
      {"abc",1}, {"def",2}, {"ghi",3}
    };
    ILookup<string, int> lookup = ids.ToLookup(x => x.Key, x => x.Value);
    int i = lookup["def"].Single();

15
enum Constants
{
    Abc = 1,
    Def = 2,
    Ghi = 3
}

...

int i = (int)Enum.Parse(typeof(Constants), "Def");

2
재미있는 아이디어! Parse () 호출이 얼마나 잘 수행되는지 궁금합니다. 나는 프로파일 러 만이 그 대답을 할 수 있을까 걱정합니다.
David Schmitt

10

이것은 "CONST Dictionary"에 접근 할 수있는 가장 가까운 것입니다.

public static int GetValueByName(string name)
{
    switch (name)
    {
        case "bob": return 1;
        case "billy": return 2;
        default: return -1;
    }
}

컴파일러는 코드를 최대한 깨끗하게 빌드 할 수있을 정도로 똑똑합니다.


8

4.5+ 프레임 워크를 사용하는 경우 ReadOnlyDictionary (목록의 경우 ReadOnly Collection)를 사용하여 읽기 전용 매핑 / 상수를 수행합니다. 다음과 같은 방식으로 구현됩니다.

static class SomeClass
{
    static readonly ReadOnlyDictionary<string,int> SOME_MAPPING 
        = new ReadOnlyDictionary<string,int>(
            new Dictionary<string,int>()
            {
                { "One", 1 },
                { "Two", 2 }
            }
        )
}        

private static readonly Dictionary<string, string> _yourDictionaryName = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase) { { "One",1 }, { "Two",2 }, { "Three",3 }, };이것이 내가 한 방법입니다.
Sanket Sonavane '17

@SanketSonavane a readonly Dictionary<TKey, TValue>는 a 와 동일하지 않습니다 ReadOnlyDictionary<TKey, TValue>. 실제로, "읽기 전용"방식은 서로 거의 반대입니다. dotnetfiddle.net/v0l4aA를 참조하십시오 .
Broots Waymb

2

네임 스페이스 나 클래스를 사용하여 값을 중첩하지 않는 이유는 무엇입니까? 불완전 할 수 있지만 매우 깨끗합니다.

public static class ParentClass
{
    // here is the "dictionary" class
    public static class FooDictionary
    {
        public const string Key1 = "somevalue";
        public const string Foobar = "fubar";
    }
}

이제 .ParentClass.FooDictionary.Key1 등에 액세스 할 수 있습니다.


이것은 IDataErrorInfo 인터페이스를 구현하지 않기 때문입니다.
David Schmitt

1

사전에 대한 변경 불가능한 표준 인터페이스가없는 것 같으므로 래퍼를 만드는 것이 유감스럽게도 유일한 합리적인 옵션처럼 보입니다.

편집 : Marc Gravell은 내가 놓친 ILookup을 발견했습니다. 그래도 여전히 새 래퍼를 만들지 않아도되지만 To.Lookup ()으로 Dictionary를 변환해야합니다.

이것이 특정 시나리오로 제한되어야하는 경우보다 비즈니스 지향적 인 인터페이스를 사용하는 것이 좋습니다.

interface IActiveUserCountProvider
{
    int GetMaxForServer(string serverName);
}

1

왜 아무도 이것을 언급하지 않았지만 C #에서 const를 할당 할 수없는 것에 대해서는 정적 읽기 전용 속성을 사용합니다.

예:

public static readonly Dictionary<string, string[]> NewDictionary = new Dictionary<string, string[]>()
        {
            { "Reference1", Array1 },
            { "Reference2", Array2 },
            { "Reference3", Array3 },
            { "Reference4", Array4 },
            { "Reference5", Array5 }
        };

사전의 내용은 여전히 ​​변경 가능하고 런타임시 할당되기 때문입니다.
David Schmitt

0

winforms 콤보 상자에 바인딩 한 이후의 또 다른 아이디어 :

public enum DateRange {
    [Display(Name = "None")]
    None = 0,
    [Display(Name = "Today")]
    Today = 1,
    [Display(Name = "Tomorrow")]
    Tomorrow = 2,
    [Display(Name = "Yesterday")]
    Yesterday = 3,
    [Display(Name = "Last 7 Days")]
    LastSeven = 4,
    [Display(Name = "Custom")]
    Custom = 99
    };

int something = (int)DateRange.None;

표시 이름 에서 int 값을 얻으려면 :

public static class EnumHelper<T>
{
    public static T GetValueFromName(string name)
    {
        var type = typeof(T);
        if (!type.IsEnum) throw new InvalidOperationException();

        foreach (var field in type.GetFields())
        {
            var attribute = Attribute.GetCustomAttribute(field,
                typeof(DisplayAttribute)) as DisplayAttribute;
            if (attribute != null)
            {
                if (attribute.Name == name)
                {
                    return (T)field.GetValue(null);
                }
            }
            else
            {
                if (field.Name == name)
                    return (T)field.GetValue(null);
            }
        }

        throw new ArgumentOutOfRangeException("name");
    }
}

용법:

var z = (int)EnumHelper<DateRange>.GetValueFromName("Last 7 Days");

-2

왜 안되 겠어요 :

public class MyClass
{
    private Dictionary<string, int> _myCollection = new Dictionary<string, int>() { { "A", 1 }, { "B", 2 }, { "C", 3 } };

    public IEnumerable<KeyValuePair<string,int>> MyCollection
    {
        get { return _myCollection.AsEnumerable<KeyValuePair<string, int>>(); }
    }
}

3
지정된 조건에서 사용 가능한 대안에 비해 상당히 비싸기 때문입니다.
David Schmitt

또한 컴파일되지 않기 때문에
AustinWBryan

@AustinWBryan 예는 컴파일
derHugo
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.