2016-12-13 3 views
0

Является ли более эффективным использование перечислений вместо строковых массивов, с точки зрения производительности?Методы enum vs string методы

Я решил протестировать определенный метод IsDefined, вместо проверки соответствия в массиве строк. Я создал объект секундомера для проверки времени выполнения для каждого из них.
код ниже:

Определено перечисление за пределами класса Main:

enum Color : byte { red, blue, green } 

Внутри Main:

string[] colArr = new string[] { "red", "blue", "green" }; 
string input = "green"; 
Stopwatch s1 = new Stopwatch(); 
int loopIterations = 0; 

s1.Restart(); 
while (loopIterations++ < 100000000) 
    foreach (var blah in colArr) 
     if (blah == input) 
      break; 
s1.Stop(); 
Console.WriteLine("Runtime for foreach loop: {0}", s1.Elapsed); 

loopIterations = 0; 
s1.Restart(); 
while (loopIterations++ < 100000000) 
    if (Enum.IsDefined(typeof(Color), input)) 
     continue; 
s1.Stop(); 
Console.WriteLine("Runtime for IsDefined method returned value: {0}", s1.Elapsed); 

И мой результат выглядит следующим образом:

Runtime for foreach loop: 00:00:01.4862817 
Runtime for IsDefined method returned value: 00:00:09.3421654 
Press any key to continue . . . 

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

+1

Оба имеют свою собственную цель. Вы используете перечисления для API-интерфейсов, отличных от разработчиков, и вы используете строки для ввода пользователем. Что показали ваши исследования? – CodeCaster

+3

Вы используете перечисление как строку, конечно, это менее эффективно. Вместо этого сохраните его как 'Color', f.e .:« Color input = Color.green; ». Тогда вам не нужно проверять, существует ли он. –

+1

Вызов 'Enum.IsDefined (typeof (Color), input)', вероятно, будет фальсифицировать ваши вычисления, поскольку для этого требуется разобрать строку и сделать некоторые распаковки. Вы можете использовать 'Enum.GetValues' как эквивалент для' foreach (var blah in colArr) ', чтобы получить более правильный результат. – HimBromBeere

ответ

2

Для начинающих, а не для производительности, большой причиной использования перечислений по строкам является ремонтопригодность кода. Например, попытка «найти все ссылки» на Color.red может быть выполнена несколькими щелчками мыши в визуальной студии. Пытаться найти струны не так просто. Всегда печатать строки также подвержено ошибкам. Хотя обе проблемы можно было облегчить, используя константы, проще использовать перечисления.

Перечисление можно рассматривать как постоянное целочисленное значение, которое имеет хорошую производительность и имеет такие преимущества, как использование флагов (масок). Сравнение int будет быстрее, чем сравнение строки, но это не то, что здесь происходит. В основном вы хотите что-то сделать для определенного значения, и вы можете проверить if(someString == "red") против if(someColVal == Color.red), и в этом случае последнее должно быть быстрее.

Проверка того, существует ли значение в перечислении, может быть медленнее с Enum.IsDefined, но эта функция должна искать значения enum каждый раз в этом цикле. Между тем первый тест имеет предварительно определенный массив. Для строгого сравнения производительности до первого теста, вы могли бы сделать что-то вроде:

var colvalues = Enum.GetValues(typeof(Color)).Cast<Color>().ToArray(); // or hardcode: var colvalues = new[]{Color.red, Color.blue, Color.green}; 
var colinput = Color.red; 
while (loopIterations++ < 100000000) 
    foreach (var blah in colvalues) 
     if (blah == colinput) 
      break; 

Хотя, как указано, находя, если значение существует в enum обычно не является его основной функцией (в основном он используется для проверки для конкретное значение).Тем не менее, это целое основание позволяет для других методов, чтобы проверить, является ли значение в ожидаемом диапазоне, например, маски-проверке или >, >=, < или <=

редактировать видя комментарии о пользовательском вводе: в основном, вход будет управляться, например: пользователю отображается меню. В среде консоли это меню можно было построить с номерами перечисления. Например, перечисление enum Color : byte { red = 1, blue, green }, меню 1. красный 2. синяя 3. зеленый вход

Пользователь должен быть целым числом. С другой стороны, если требуется ввести текст, IsDefined будет препятствовать повторному значению значений и подходит для удобства использования. Для исполнения имена могут быть буферизованы чем-то вроде var colvalues = Enum.GetNames(typeof(Color)).ToArray();

+0

Название: TBH, я бы не сказал, что использование масок - это преимущество. ИМХО нелегко поддерживать и приводит к большей путанице и ошибкам кода, чем любые предполагаемые выгоды. Вероятно, это имело смысл в 1990-х годах, ограниченном памятью, но в сегодняшнем мире нам, вероятно, лучше поддерживать «выбранные» перечисления в коллекциях. – code4life

+0

@ code4life Лично я люблю флаги-перечисления с масками. (Для одиночных значений как цвета они были бы бесполезны, конечно). Не из-за ограничений памяти, но вы можете легко добавлять или удалять значения. например свойство border-enum: 'someClass.Border | = Border.Left' устанавливает левую границу независимо от того, уже ли она установлена. Если вы сделаете это с помощью коллекции, вы можете получить бесконечные записи, если вы не добавите дополнительный код, чтобы проверить, действительно ли значение уже находится в коллекции. –

+0

Спасибо :) Ответ очень ясный и тщательный. +1 –

1

Нормальное использование для перечислений - представлять логические состояния или ограниченный диапазон параметров: в вашем примере, если, например, продукт когда-либо выпускается только в трех цветах. Использование строки для представления цветов в таком случае имеет два недостатка: вы можете ошибочно называть имя цвета где-то в вашем коде, и усложнять отслеживание ошибок; и сравнение строк по сути медленнее, чем сравнение перечислений (что в основном сравнивает целые числа).

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

+0

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

+0

@ Когда вы можете преобразовать строку в enum ** один раз **, а затем использовать IsDefined. То, что IsDefined делает внутренне. Он преобразует строку в перечисление один раз и затем начинает сравнивать. Когда вы помещаете это в цикл, преобразование также включается в цикл. Так что ваш тест неверен. Потому что вы измеряете накладные расходы на преобразование строки в перечисление. –

+0

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

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