2010-07-15 2 views
22

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

Но, я действительно понимаю, что это происходит с несколькими перечислениями и множеством отливок из «int» и «string» в мои переписные записи и из них.

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

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

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

+3

Зачем вам нужно их бросать? – LukeH

+2

@ LukeH: Предположительно потому, что в базе данных значения будут целыми. –

+0

@ Jon: Это то, что я догадался, но было бы неплохо получить разъяснения от OP. – LukeH

ответ

29

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

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

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

+0

Это может быть еще один вопрос, но связанный с ним: переводит перечисления друг в друга с соответствующими значениями «int», столь же дешевыми, как и '(int)' cast? –

+1

@CADBloke: Я бы так ожидал, да. Но попробуйте, если это важно для вас :) –

13

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

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

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

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

Что касается аргумента производительности: Имейте в виду, что вы не просто отливку Enum к междунар: Вы также работаете 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 

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

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

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

В отличие от Java, C# не автоматически проверять значения броска, чтобы убедиться, что они являются действительными (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); 
    } 
} 

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

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

[ворчание]

+1

+1, я полностью согласен с «не совсем непреложной» частью. Вы будете ненавидеть, если ваше десятилетнее приложение основано на политической модели из прошлого, и вы больше не получите ее поддержки ... –

+1

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

+2

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

0

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

+5

В представлениях обычно используется 4 байта - их базовый тип по умолчанию - 'int', если вы явно не объявляете иначе. – LukeH

+1

«Значительно превосходящая» часть во многом зависит от того, что вы делаете с перечислением, как только вы ее получите. Если вы используете его в инструкции switch, перечисления будут быстрее. Если вы назовете ToString на нем, будет существовать более популярный словарь. См. Мой ответ. – StriplingWarrior

-2

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

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

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

+0

Перечисления в C# не являются объектами. Они действительно не происходят из базового класса или реализуют интерфейс. Это просто стабильные по типу константы. Они могут быть полезны при использовании как таковые, но они опасны, если их неправильно понимать. – StriplingWarrior

+0

Я знаю, что перечисления не являются объектами. Вот почему я говорю, что они бесполезны. Во всяком случае, я дал ясный ответ. – onof

2

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

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

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 и во Франции.

Cheers!

+0

Поскольку целые значения отображаются в базе данных, было бы полезно указать явные значения int, поэтому они не меняются, если они будут переупорядочены. – StriplingWarrior

+0

Ну, я думаю, что было бы не слишком сложно изменить код, чтобы это можно было сделать. –

0

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

+0

Как использовать константы здесь? – StriplingWarrior

Смежные вопросы