Объединение членов перечисления в C#

Скажем, у меня есть enum BasicType, который определяется следующим образом:

    public enum ObjectType{
        A = 1,
        B = 2,
        C = 3,
    }

Идентификатор BasicType выполняет троичную классификацию любого Object. Впоследствии я понял, что с объектами A и B нужно обращаться так же, как с C, поэтому я определил еще один enum ObjectGroupType как следует:

public enum ObjectGroupType
{
   AB = 1,
   C = 2,
}

С новым перечислением я могу объединять объекты нескольких известных типов в один. Итак, когда я получаю поток объектов различных типов, я фактически определяю, относятся ли они к типу AB или C. Есть ли элегантный обходной путь для этого? Например, смогу ли я присвоить одинаковые значения перечисления для A и B в ObjectGroupType?:

Редактировать 1: я не могу найти сходство с вопросом здесь

Редактировать 2. Спасибо, Морис, за ваш конструктивный вклад. Принимая во внимание ваш ответ, я придумал это новое определение ObjectGroupType.

 public enum ObjectGroupType
 {
    AB = ObjectType.A | ObjectType.B
    C = 2,
 }

Это действительно?

По существу, когда я обрабатываю поток объектов типа AB, я хочу установить объекты типа A или типа B. Это очень похоже на иерархическое двухуровневое дерево решений:

    object
    /     \
 AB        C
 /\
A  B

person envy_intelligence    schedule 24.07.2016    source источник
comment
У вас есть рабочий пример того, как вы хотели бы использовать этот enum?   -  person Jace    schedule 24.07.2016
comment
Почему вы отметили java?   -  person ganesshkumar    schedule 24.07.2016
comment
Это похоже на дубликат stackoverflow.com/questions/8043027/non- уникальные значения перечисления   -  person Olathe    schedule 24.07.2016
comment
Если ваше исходное перечисление было A = 1, B = 2, C = 4, которые являются степенью двойки, то любой X + Y будет уникальным. Итак, АВ = 3.   -  person jdweng    schedule 24.07.2016
comment
@jdweng, к сожалению, это не так.   -  person envy_intelligence    schedule 24.07.2016


Ответы (3)


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

Учитывая перечисление, которое выглядит так:

[Flags]
enum ObjectType
{
    A = 1,
    B = 2,
    C = 4,
    D = 8
}

Вы можете установить значение сравнения, которое является побитовым ИЛИ нескольких значений:

var allowedValues = ObjectType.A | ObjectType.C;

Это работает, потому что значения в перечислении действуют как битовые поля под прикрытием.

Когда вы запускаете свой код, вы выполняете побитовое И для переменной allowValues ​​и тестовой переменной и смотрите, соответствует ли она вашей тестовой переменной. Если это так, то это одно из значений, которые вы хотите:

if ((test & allowed) == test) ...

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

void Main()
{
    var allowed = ObjectType.A | ObjectType.C;

    var values = new int [] { 1, 2, 4, 8 };

    foreach (var i in values)
    {
        var test = (ObjectType)i;

        if ((test & allowed) == test)
        {
            Console.WriteLine("Found a match: {0}", test);
        }
        else
        {
            Console.WriteLine("No match: {0}", test);
        }
    }
}

Удачи!

person Maurice Reeves    schedule 24.07.2016
comment
Это очень интересное решение. Как бы вы извлекли имена каждого типа объекта (в данном случае A и C)? Я считаю, что Enum.GetName(allowed) не сработает. - person envy_intelligence; 24.07.2016
comment
Если вы запустите пример, вы увидите, что он на самом деле выводит имя для перечисления, и если вы напишете в консоли «разрешено», он также должен показать вам объединенные значения. - person Maurice Reeves; 24.07.2016

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

Я нашел ответ Мориса Ривза очень хорошим, я только хочу предоставить дополнительную информацию:

   [Flags]
    public enum ObjectType
{
    None=0,
    A = 1,
    B = 2,
    C = 4,
    D = 8,
    E = 16,
    AorB=A|B,
    BorCorD=B|C|D,

}

Используя атрибут [Flags], вы можете создавать наборы элементов перечисления, которые могут помочь вам установить разные бизнес-правила для каждого набора.

Чтобы проверить, существует ли предмет в наборе, вы можете сделать следующее:

public static bool IsAorB(this ObjectType item)
{
      return ObjectType.AorB.HasFlag(item);
}

если вы хотите создать на лету новый набор элементов, вы можете сделать:

var newGroup=ObjectType.A | ObjectType.BorCorD;

если вы хотите применить какое-то бизнес-правило к набору, кроме элемента, вы можете сделать:

var newGroupExceptC =newGroup^=ObjectType.C;

Теперь, если вы проверите, существует ли элемент C в наборе, вы получите false:

bool exist=newGroupExceptC.HasFlag(ObjectType.C) // =false

дополнительную информацию вы можете найти здесь

person Lucian Bumb    schedule 24.07.2016
comment
Я независимо придумал такой же подход, но тип возвращаемого значения bool не масштабируется — что, если бы появился новый третий тип событий {D, E}. Итак, моя цель — сгруппировать по типам. - person envy_intelligence; 24.07.2016
comment
Мой пример был методом расширения, чтобы проверить, является ли groupAorB, но вы можете сделать столько расширений, сколько хотите/нужно. В конце нужно проверить тип группы, не так ли? - person Lucian Bumb; 24.07.2016

Вы можете использовать int вместо перечисления, использовать значения, которые не перекрываются при объединении (т. е. значения, двоичное представление которых имеет только один бит), а затем выполнить операцию маски над ObjectType параметра, чтобы определить, является ли он AB:

class SomeClass
{
    public static class ObjectType
    {
        public const int A = 1;
        public const int B = 2;
        public const int C = 4;
        public const int D = 8;
    }

    public int MyType;
    public string Title;
    static void Main(string[] args)
    {
        List<SomeClass> list = new List<SomeClass>()
        {
            new SomeClass() {Title ="I am of type A", MyType = ObjectType.A }
            ,new SomeClass() {Title ="I am of type B", MyType = ObjectType.B }
            ,new SomeClass() {Title ="I am of type AB", MyType = ObjectType.A | ObjectType.B }
        };

        list.ForEach(p => { if (p.MyType == (ObjectType.A | ObjectType.B)) Console.WriteLine(p.Title); });
    }
}

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

person user3248578    schedule 24.07.2016