Мое приложение имеет много различных справочных значений, эти значения никогда не изменяются, например, штаты США. Вместо того, чтобы помещать их в таблицы базы данных, я хотел бы использовать перечисления.
Но, я действительно понимаю выполнение его, этот путь включает наличие нескольких перечислений и большого количества кастинга от "интервала" и "строки" к и от моих перечислений.
Альтернатива, я вижу, что кто-то упомянул, что использовал Словарь <> как справочные таблицы, но перечислимая реализация, кажется, является более чистой.
Так, я хотел бы спросить при хранении и раздавании большого количества перечислений и кастинге их быть проблемой к производительности, или я должен использовать подход справочных таблиц, который работает лучше?
Править: Кастинг необходим как идентификатор, который будет сохранен в других таблицах базы данных.
Преобразование int
в перечисление чрезвычайно дешево ... это будет быстрее, чем поиск по словарю. По сути, это не операция, просто копирование битов в место с другим условным типом.
Разбор строки в значение перечисления будет несколько медленнее.
Я сомневаюсь, что это станет для вас узким местом, однако вы делаете это, хотя, честно говоря ... не зная больше о том, что вы делаете, довольно сложно давать рекомендации, выходящие за рамки обычного "напишите простейшее, режим читаемый и поддерживаемый код, который будет работать, затем проверьте, что он работает достаточно хорошо ».
Перечисления значительно превосходят почти все, особенно словари. В перечислениях используется только один байт. Но зачем вам кастинг? Похоже, вам следует использовать перечисления везде.
Вы не заметите большой разницы в производительности, но я все же рекомендую использовать словарь, потому что это даст вам немного больше гибкости в будущем.
Во-первых, 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# не были разработаны для обеспечения сопоставлений между значениями и строками. Они были разработаны для обеспечения сильно типизированных константных значений, которые вы можете передавать в коде. Два основных преимущества этого:
В отличие от 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);
}
}
Таким образом:
Enum.ToString()
. [grunt]
В качестве альтернативы вы можете использовать константы
По возможности избегайте перечисления: перечисления следует заменять одиночными, производными от базового класса или реализующими интерфейс.
Практика использования enum проистекает из программирования C в старом стиле.
Вы начинаете использовать enum для штатов США, затем вам понадобится количество жителей, столица ..., и вам понадобится много больших переключателей, чтобы получить всю эту информацию.
Вы можете использовать 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 и на французском языке.
Ура!