Создание постоянного словаря в C #

Каков наиболее эффективный способ создать постоянное (никогда не изменяется во время выполнения) сопоставление strings с ints?

Я пробовал использовать словарь const, но это не сработало.

Я мог бы реализовать неизменяемую оболочку с соответствующей семантикой, но это все еще кажется не совсем правильным.


Для тех, кто спросил, я реализую IDataErrorInfo в сгенерированном классе, и я ищу способ выполнить поиск columnName в моем массиве дескрипторов.

Я не знал (опечатка при тестировании! Ох!), Что переключатель принимает строки, так что я собираюсь использовать его. Спасибо!


person David Schmitt    schedule 06.11.2008    source источник
comment
здесь есть решение: stackoverflow.com/questions/10066708/   -  person    schedule 16.08.2013


Ответы (10)


Создание действительно сгенерированного во время компиляции константного словаря в C # на самом деле не является простой задачей. На самом деле ни один из ответов здесь не дает этого.

Однако есть одно решение, которое отвечает вашим требованиям, хотя и не обязательно хорошее; помните, что в соответствии со спецификацией C # таблицы вариантов переключения компилируются в таблицы переходов с постоянным хешем. То есть это постоянные словари, а не набор операторов if-else. Итак, рассмотрим такой оператор switch-case:

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

Это именно то, что вам нужно. И да, я знаю, это некрасиво.

person Tamas Czinege    schedule 06.11.2008
comment
В любом случае это сгенерированный код, имеет хорошие характеристики производительности, может быть вычислен во время компиляции, а также имеет другие полезные свойства для моего варианта использования. Я буду так делать! - person David Schmitt; 06.11.2008
comment
просто будьте осторожны, возвращение типов, не являющихся значениями, даже в таком случае, нарушит неизменяемость ваших классов. - person Tom Anderson; 18.04.2010
comment
switch-case великолепен, пока вам не нужно перебирать значения. - person Alex from Jitbit; 28.07.2011
comment
Я пришел сюда, думая, что мне нужен постоянный словарь, чтобы помочь сопоставить данные между аналогичными классами. Вместо этого мне нужен был метод, содержащий переключатель! Кроме того, теперь мне может потребоваться этот метод в интерфейсе для любых будущих классов сопоставления данных. Этот ответ был точным. - person MrOodles; 13.03.2013
comment
В нем отсутствует множество приятных функций, которые есть в словарях. - person AsyncMoksha; 21.02.2014
comment
@TomAnderson: конструкция будет вести себя как неизменяемая, если возвращаемые элементы являются типами значений (изменяемыми или нет) или если они являются неизменяемыми объектами класса. - person supercat; 18.03.2015

В текущем фреймворке очень мало неизменяемых коллекций. Я могу вспомнить один относительно безболезненный вариант в .NET 3.5:

Используйте Enumerable.ToLookup() - класс Lookup<,> является неизменным (но многозначным на правый); вы можете легко сделать это из 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();
person Marc Gravell    schedule 06.11.2008

Это самое близкое к "словарю CONST":

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

Компилятор будет достаточно умен, чтобы собрать код как можно более чистым.

person Timothy Khouri    schedule 06.11.2008

При использовании 4.5+ Framework я бы использовал 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 }
            }
        )
}        
person Kram    schedule 30.10.2018
comment
private static readonly Dictionary<string, string> _yourDictionaryName = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase) { { "One",1 }, { "Two",2 }, { "Three",3 }, }; Вот как я это сделал. - person Sanket Sonavane; 17.12.2019
comment
@SanketSonavane readonly Dictionary<TKey, TValue> не эквивалентно ReadOnlyDictionary<TKey, TValue>. Фактически, их способ, которым они доступны только для чтения, в значительной степени противоположен друг другу. См. dotnetfiddle.net/v0l4aA. - person Broots Waymb; 05.03.2020

Почему бы не использовать пространства имен или классы для вложенности ваших значений? Он может быть несовершенным, но очень чистый.

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 и т. Д.

person Joshua    schedule 10.04.2018
comment
Поскольку это не реализует интерфейс IDataErrorInfo. - person David Schmitt; 11.04.2018

Я не уверен, почему никто не упомянул об этом, но в C # для вещей, которым я не могу назначить const, я использую статические свойства только для чтения.

Пример:

public static readonly Dictionary<string, string[]> NewDictionary = new Dictionary<string, string[]>()
        {
            { "Reference1", Array1 },
            { "Reference2", Array2 },
            { "Reference3", Array3 },
            { "Reference4", Array4 },
            { "Reference5", Array5 }
        };
person Suleman    schedule 08.08.2019
comment
потому что содержимое словаря по-прежнему изменяемо и выделяется во время выполнения. - person David Schmitt; 15.08.2019

Кажется, что не существует стандартного неизменяемого интерфейса для словарей, поэтому, к сожалению, создание оболочки кажется единственным разумным вариантом.

Изменить: Марк Грейвелл нашел ILookup, который я пропустил - это позволит вам, по крайней мере, избежать создания новой оболочки, хотя вам все равно нужно преобразовать словарь с помощью .ToLookup ().

Если это необходимость, ограниченная конкретным сценарием, вам может быть лучше с интерфейсом, более ориентированным на бизнес-логику:

interface IActiveUserCountProvider
{
    int GetMaxForServer(string serverName);
}
person Sander    schedule 06.11.2008

Еще одна идея, поскольку я привязываюсь к комбинированному списку 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");
person Gina Marano    schedule 24.10.2019

Почему нет:

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>>(); }
    }
}
person Community    schedule 27.07.2010
comment
Потому что это довольно дорого по сравнению с доступными альтернативами при указанных условиях. - person David Schmitt; 27.07.2010
comment
Также потому, что это даже не компилируется - person AustinWBryan; 24.06.2018
comment
@AustinWBryan да, он компилируется - person derHugo; 17.08.2018

person    schedule
comment
Интересная идея! Мне интересно, насколько хорошо работает вызов Parse (). Боюсь, что только профилировщик может ответить на этот вопрос. - person David Schmitt; 16.12.2008