Подсчет количества флагов, установленных в перечислении
Я уверен, что должен быть намного лучший способ сделать это. Я пытаюсь выполнить операцию подсчета в перечислении Flags. Прежде чем я перебирал все возможные значения и считал успешные операции И.
например.
[Flags]
public enum Skills
{
None = 0,
Skill1 = 1,
Skill2 = 2,
Skill3 = 4,
Skill4 = 8,
Skill5 = 16,
Skill6 = 32,
Skill7 = 64,
Skill8 = 128
}
public static int Count(Skills skillsToCount)
{
Skills skill;
for (int i = 0; i < SkillSet.AllSkills.Count; i++)
{
skill = SkillSet.AllSkills[i];
if ((skillsToCount & skill) == skill && skill != Skills.None)
count++;
}
return count;
}
Я уверен, что должен быть лучший способ сделать это, хотя и должен страдать от ментального блока. Может ли кто-нибудь посоветовать более приятное решение?
Ответы
Ответ 1
После просмотра сайта Assaf предложил мне немного другое решение, которое я получил для Int32.
Здесь код для кого-то еще:
internal static UInt32 Count(this Skills skills)
{
UInt32 v = (UInt32)skills;
v = v - ((v >> 1) & 0x55555555); // reuse input as temporary
v = (v & 0x33333333) + ((v >> 2) & 0x33333333); // temp
UInt32 c = ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24; // count
return c;
}
Ответ 2
Следующий код даст вам количество бит, заданных для заданного числа любого типа, размер которого зависит от байта до длинного.
public static int GetSetBitCount(long lValue)
{
int iCount = 0;
//Loop the value while there are still bits
while (lValue != 0)
{
//Remove the end bit
lValue = lValue & (lValue - 1);
//Increment the count
iCount++;
}
//Return the count
return iCount;
}
Этот код очень эффективен, поскольку он выполняет только итерацию один раз для каждого бита, а не один раз для каждого возможного бита, как в других примерах.
Ответ 3
Счет эквивалентен подсчету того, сколько бит установлено в 1 в целочисленном значении перечисления.
В C/С++ есть очень быстрые способы сделать это, что вы можете адаптировать к С#:
например.
int bitcount(unsigned int n) {
/* works for 32-bit numbers only */
/* fix last line for 64-bit numbers */
register unsigned int tmp;
tmp = n - ((n >> 1) & 033333333333)
- ((n >> 2) & 011111111111);
return ((tmp + (tmp >> 3)) & 030707070707) % 63;
}
Взято из здесь.
ИЗМЕНИТЬ
Если ссылка мертва. Нашел другой, который, вероятно, содержит один и тот же контент.
Ответ 4
Очень сжатый способ сделать это с помощью BitArray
и LINQ:
public static int Count(Skills skillsToCount)
{
return new BitArray(new[] {(int)skillsToCount}).OfType<bool>().Count(x => x);
}
Ответ 5
<FlagsAttribute()> _
Public Enum Skills As Byte
None = 0
Skill1 = 1
Skill2 = 2
Skill3 = 4
Skill4 = 8
Skill5 = 16
Skill6 = 32
Skill7 = 64
Skill8 = 128
End Enum
Dim x As Byte = Skills.Skill4 Or Skills.Skill8 Or Skills.Skill6
Dim count As Integer
If x = Skills.None Then count = 0 Else _
count = CType(x, Skills).ToString().Split(New Char() {","c}, StringSplitOptions.RemoveEmptyEntries).Count
зависит от определения "лучше".
проверка для Skills.None требуется, потому что, если бит не включен, строка() возвращает Skills.None, которая приводит к подсчету 1. это будет работать одинаково для целых, длинных и их неподписанных родственников.
Ответ 6
Единственная причина использования этого метода - это если флаги не смежны и если флаги будут добавляться периодически.
<FlagsAttribute()> _
Public Enum Skills As Integer
Skill1 = CInt(2 ^ 0) 'bit 0
Skill2 = CInt(2 ^ 1)
Skill3 = CInt(2 ^ 2)
Skill4 = CInt(2 ^ 3)
Skill5 = CInt(2 ^ 4)
Skill6 = CInt(2 ^ 5)
Skill7 = CInt(2 ^ 6)
Skill8 = CInt(2 ^ 7)
Skillx = CInt(2 ^ 10) 'bit 10, some bits were skipped
End Enum
Dim mySkills As Integer = Skills.Skillx Or Skills.Skill4 Or Skills.Skill8 Or Skills.Skill6
Dim count As Integer 'count of bits on
count = CType(mySkills, Skills).ToString().Split(New Char() {","c}, _
StringSplitOptions.RemoveEmptyEntries).Count
если "лучше" означает, что это не так;) это.
Ответ 7
int count = Enum.GetValues(typeof(Skills)).Length;
Ответ 8
Enum.GetNames() вернет массив всех имен в перечислении, добавит .Length, чтобы найти счет.