Створення постійного словника в C #


128

Який найефективніший спосіб створити постійне (ніколи не змінюється під час виконання) відображення strings на ints?

Я спробував використовувати словник const , але це не вийшло.

Я міг би реалізувати незмінну обгортку з відповідною семантикою, але це все ще не здається абсолютно правильним.


Для тих, хто запитав, я реалізую IDataErrorInfo в створеному класі і шукаю спосіб перетворити колонкуName в мій масив дескрипторів.

Мені не було відомо (помилка друку під час тестування! D'oh!), Що перемикач приймає рядки, тому це я буду використовувати. Дякую!


1
є рішення тут: stackoverflow.com/questions/10066708 / ...

Відповіді:


180

Створення справжнього постійного словника, створеного в компільований час у C #, насправді не є простою задачею. Власне, жодної з відповідей тут насправді цього не досягти.

Є одне рішення, яке відповідає вашим вимогам, хоча й не обов'язково приємне; пам’ятайте, що згідно зі специфікацією C #, таблиці регістру комутаторів складаються в таблиці постійних хеш-стрибків. Тобто, це постійні словники, а не ряд тверджень if-else. Тож розглянемо такий випадок випадку переключення:

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

Це саме те, що ви хочете. І так, я знаю, це некрасиво.


9
У будь-якому випадку він генерується кодом, має хороші характеристики продуктивності, може бути обчислений за часом компіляції та має інші приємні властивості і для мого випадку використання. Я буду робити це так!
Девід Шмітт

4
просто будьте обережні, що повернення нецінні типів, навіть як це, порушить незмінність ваших класів.
Том Андерсон

35
case-case є приголомшливим, поки вам не потрібно "передбачати" значення.
Алекс

6
Не вистачає багато приємних функцій, які мають словники.
Зінан Сін

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 (). Я побоююся, що тільки профілер може відповісти на це.
Девід Шмітт

10

Це найближче, що ви можете потрапити до "Словника CONST":

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

Компілятор буде досить розумним, щоб створити код максимально чистим.


8

Якщо я використовую 4.5+ Framework, я би використовував ReadOnlyDictionary (також колекцію ReadOnly для списків), щоб робити лише відображення / константи для читання. Він реалізується наступним чином.

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

@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.
Девід Шмітт

1

Здається, не існує жодного стандартного незмінного інтерфейсу для словників, тому створення обгортки здається єдиним розумним варіантом, на жаль.

Редагувати : Марк Гравелл знайшов те, що я пропустив ILookup - це дозволить вам принаймні уникнути створення нової обгортки, хоча вам все-таки потрібно перетворити словник за допомогою .ToLookup ().

Якщо це потреба, обмежена певним сценарієм, вам може бути краще з інтерфейсом, орієнтованим на бізнес-логіку:

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

оскільки вміст словника все ще змінюється, і він виділяється під час виконання.
Девід Шмітт

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
Тому що це досить дорого порівняно з доступними альтернативами за вказаних умов.
Девід Шмітт

Також тому, що це навіть не
складається

@AustinWBryan так це компілює
derHugo
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.