Enum и производительность - PullRequest
24 голосов
/ 15 июля 2010

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

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

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

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

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

Ответы [ 6 ]

32 голосов
/ 15 июля 2010

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

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

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

15 голосов
/ 15 июля 2010

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

С одной стороныEnum в C # не может автоматически иметь связанный с ним класс, как в 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

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

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

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

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

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

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]

2 голосов
/ 15 июля 2010

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

Ура!

0 голосов
/ 15 июля 2010

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

0 голосов
/ 15 июля 2010

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

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

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

0 голосов
/ 15 июля 2010

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...