Подтвердить что ты не робот

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

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

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

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

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

Редактировать. Приведение должно выполняться в качестве идентификатора, который должен храниться в других таблицах базы данных.

4b9b3361

Ответ 1

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

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

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

Ответ 2

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

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

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

Изменить

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

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

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

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

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

В отличие от Java, С# автоматически не проверяет значения литых, чтобы убедиться, что они действительны (myState = (States)321), поэтому вы не получаете никаких проверок данных во время выполнения, не делая их вручную. Если у вас нет кода, который явно ссылается на состояния ( "States.Oklahoma" ), то вы не получите никакого значения из № 2 выше. Это оставляет нас С# 1 как единственной реальной причиной использования перечислений. Если это достаточно хорошая причина для вас, я бы предложил использовать перечисления вместо 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);
    }
}

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

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

[ворчание]

Ответ 3

Вы можете использовать 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 и французский, хотя.

Приветствия!

Ответ 4

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

Ответ 5

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

Ответ 6

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

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

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