Перечисление и производительность

Мое приложение имеет много различных справочных значений, эти значения никогда не изменяются, например, штаты США. Вместо того, чтобы помещать их в таблицы базы данных, я хотел бы использовать перечисления.

Но, я действительно понимаю выполнение его, этот путь включает наличие нескольких перечислений и большого количества кастинга от "интервала" и "строки" к и от моих перечислений.

Альтернатива, я вижу, что кто-то упомянул, что использовал Словарь <> как справочные таблицы, но перечислимая реализация, кажется, является более чистой.

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

Править: Кастинг необходим как идентификатор, который будет сохранен в других таблицах базы данных.

26
задан abatishchev 30 July 2012 в 11:57
поделиться

6 ответов

Преобразование int в перечисление чрезвычайно дешево ... это будет быстрее, чем поиск по словарю. По сути, это не операция, просто копирование битов в место с другим условным типом.

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

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

35
ответ дан 28 November 2019 в 07:04
поделиться

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

0
ответ дан 28 November 2019 в 07:04
поделиться

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

Во-первых, Enum в C# не может автоматически иметь класс, связанный с ним, как в Java, поэтому если вы хотите связать дополнительную информацию со штатом (полное название, столица, почтовая аббревиатура и т.д.), создание класса UnitedState облегчит упаковку всей этой информации в одну коллекцию.

Кроме того, даже если вы думаете, что это значение никогда не изменится, оно не является абсолютно неизменным. Можно предположить, что у вас может появиться новое требование включить территории, например. Или, может быть, вам понадобится разрешить канадским пользователям видеть названия канадских провинций вместо этого. Если вы обращаетесь с этой коллекцией как с любой другой коллекцией данных (используя хранилище для получения значений из нее), то позже у вас будет возможность изменить реализацию хранилища, чтобы получать значения из другого источника (базы данных, веб-службы, сессии и т.д.). Перечисления гораздо менее универсальны.

Редактировать

Что касается аргумента о производительности: Помните, что вы не просто приводите Enum к int: вы также выполняете ToString() для этого enum, что добавляет значительное время обработки. Рассмотрим следующий тест:

const int C = 10000;
int[] ids = new int[C];
string[] names = new string[C];
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i< C; i++)
{
    var id = (i % 50) + 1;
    names[i] = ((States)id).ToString();
}
sw.Stop();
Console.WriteLine("Enum: " + sw.Elapsed.TotalMilliseconds);
var namesById = Enum.GetValues(typeof(States)).Cast<States>()
                .ToDictionary(s => (int) s, s => s.ToString());
sw.Restart();
for (int i = 0; i< C; i++)
{
    var id = (i % 50) + 1;
    names[i] = namesById[id];
}
sw.Stop();
Console.WriteLine("Dictionary: " + sw.Elapsed.TotalMilliseconds);

Результаты:

Enum: 26.4875
Dictionary: 0.7684

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

Enums в C# не были разработаны для обеспечения сопоставлений между значениями и строками. Они были разработаны для обеспечения сильно типизированных константных значений, которые вы можете передавать в коде. Два основных преимущества этого:

  1. У вас есть дополнительная подсказка, проверяемая компилятором, которая поможет вам избежать передачи аргументов в неправильном порядке и т.д.
  2. Вместо того, чтобы помещать в код "магические" значения чисел (например, "42"), вы можете сказать "States.Oklahoma", что делает ваш код более читабельным.

В отличие от Java, C# не проверяет автоматически значения cast, чтобы убедиться, что они действительны (myState = (States)321), поэтому вы не получите никаких проверок данных на входе во время выполнения, не выполнив их вручную. Если у вас нет кода, который явно ссылается на штаты ("States.Oklahoma"), то вы не получите никакого значения из #2 выше. Это оставляет нам #1 как единственную реальную причину для использования перечислений. Если для вас это достаточно веская причина, то я бы предложил использовать enums вместо ints в качестве ключевых значений. Затем, когда вам понадобится строка или другое значение, связанное с состоянием, выполните поиск по словарю.

Вот как я бы это сделал:

public enum StateKey{
    AL = 1,AK,AS,AZ,AR,CA,CO,CT,DE,DC,FM,FL,GA,GU,
    HI,ID,IL,IN,IA,KS,KY,LA,ME,MH,MD,MA,MI,MN,MS,
    MO,MT,NE,NV,NH,NJ,NM,NY,NC,ND,MP,OH,OK,OR,PW,
    PA,PR,RI,SC,SD,TN,TX,UT,VT,VI,VA,WA,WV,WI,WY,
}

public class State
{
    public StateKey Key {get;set;}
    public int IntKey {get {return (int)Key;}}
    public string PostalAbbreviation {get;set;}

}

public interface IStateRepository
{
    State GetByKey(StateKey key);
}

public class StateRepository : IStateRepository
{
    private static Dictionary<StateKey, State> _statesByKey;
    static StateRepository()
    {
        _statesByKey = Enum.GetValues(typeof(StateKey))
        .Cast<StateKey>()
        .ToDictionary(k => k, k => new State {Key = k, PostalAbbreviation = k.ToString()});
    }
    public State GetByKey(StateKey key)
    {
        return _statesByKey[key];
    }
}

public class Foo
{
    IStateRepository _repository;
    // Dependency Injection makes this class unit-testable
    public Foo(IStateRepository repository) 
    {
        _repository = repository;
    }
    // If you haven't learned the wonders of DI, do this:
    public Foo()
    {
        _repository = new StateRepository();
    }

    public void DoSomethingWithAState(StateKey key)
    {
        Console.WriteLine(_repository.GetByKey(key).PostalAbbreviation);
    }
}

Таким образом:

  1. вы получаете возможность передавать сильно типизированные значения, представляющие состояние,
  2. ваш поиск получает отказоустойчивое поведение, если ему дается неверный ввод,
  3. в будущем вы можете легко изменить местоположение фактических данных состояния,
  4. в будущем вы можете легко добавлять данные, связанные с состоянием, в класс State,
  5. в будущем вы можете легко добавлять новые состояния, территории, округа, провинции или что-либо еще.
  6. получение имени из int все еще примерно в 15 раз быстрее, чем при использовании Enum.ToString().

[grunt]

16
ответ дан 28 November 2019 в 07:04
поделиться

В качестве альтернативы вы можете использовать константы

0
ответ дан 28 November 2019 в 07:04
поделиться

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

Практика использования enum проистекает из программирования C в старом стиле.

Вы начинаете использовать enum для штатов США, затем вам понадобится количество жителей, столица ..., и вам понадобится много больших переключателей, чтобы получить всю эту информацию.

-2
ответ дан 28 November 2019 в 07:04
поделиться

Вы можете использовать TypeSafeEnum s

Вот базовый класс

Public MustInherit Class AbstractTypeSafeEnum
    Private Shared ReadOnly syncroot As New Object
    Private Shared masterValue As Integer = 0

    Protected ReadOnly _name As String
    Protected ReadOnly _value As Integer

    Protected Sub New(ByVal name As String)
        Me._name = name
        SyncLock syncroot
            masterValue += 1
            Me._value = masterValue
        End SyncLock
    End Sub

    Public ReadOnly Property value() As Integer
        Get
            Return _value
        End Get
    End Property

    Public Overrides Function ToString() As String
        Return _name
    End Function

    Public Shared Operator =(ByVal ats1 As AbstractTypeSafeEnum, ByVal ats2 As AbstractTypeSafeEnum) As Boolean
        Return (ats1._value = ats2._value) And Type.Equals(ats1.GetType, ats2.GetType)
    End Operator

    Public Shared Operator <>(ByVal ats1 As AbstractTypeSafeEnum, ByVal ats2 As AbstractTypeSafeEnum) As Boolean
        Return Not (ats1 = ats2)
    End Operator

End Class

А вот Enum:

Public NotInheritable Class EnumProcType
    Inherits AbstractTypeSafeEnum

    Public Shared ReadOnly CREATE As New EnumProcType("Création")
    Public Shared ReadOnly MODIF As New EnumProcType("Modification")
    Public Shared ReadOnly DELETE As New EnumProcType("Suppression")

    Private Sub New(ByVal name As String)
        MyBase.New(name)
    End Sub

End Class

И становится проще добавить интернационализацию.

Извините за то, что он написан на VB и на французском языке.

Ура!

2
ответ дан 28 November 2019 в 07:04
поделиться
Другие вопросы по тегам:

Похожие вопросы: