В C#, как легко отобразить перечислимые флаги от одного типа до другого?

Также посмотрите обновления в конце вопроса...

Учитывая следующую ситуацию:

[Flags]
enum SourceEnum
{
    SNone = 0x00,

    SA = 0x01,
    SB = 0x02,
    SC = 0x04,
    SD = 0x08,

    SAB = SA | SB,

    SALL = -1,
}

[Flags]
enum DestEnum
{
    DNone = 0x00,

    DA = 0x01,
    DB = 0x02,
    DC = 0x04,

    DALL = 0xFF,
}

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

В основном то, что я хочу, является чем-то как следующее:

Пример № 1

SourceEnum source = SourceEnum.SA;
DestEnum dest = Map<Source, Dest> (source);
Assert.That (dest, Is.EqualTo (DestEnum.DA));

Пример № 2

SourceEnum source = SourceEnum.SA | SourceEnum.SB;
DestEnum dest = Map<Source, Dest> (source);
Assert.That (dest, Is.EqualTo (DestEnum.DA | DestEnum.DB));

Пример № 3

SourceEnum source = SourceEnum.SAB;
DestEnum dest = Map<Source, Dest> (source);
Assert.That (dest, Is.EqualTo (DestEnum.DA | DestEnum.DB));

Пример № 4

SourceEnum source = SourceEnum.SALL;
DestEnum dest = Map<Source, Dest> (source);
Assert.That (dest, Is.EqualTo (DestEnum.DALL));

Пример № 5

SourceEnum source = SourceEnum.SD;
var ex = Assert.Throws<Exception> (() => Map<Source, Dest> (source));
Assert.That (ex.Message, Is.EqualTo ("Cannot map SourceEnum.SD to DestEnum!"));

Карта () функция могла принять делегата к обеспечению фактического отображения, но у меня все еще должно быть несколько функций для помощи такому делегату с битами...

DestEnum SourceToDestMapper (SourceEnum source)
{
    // Switch cannot work with bit fields enumeration...
    // This is to give the general idea...
    switch (source)
    {
        case SourceEnum.SNone:
            return DestEnum.DNone;

        case SourceEnum.SA:
            return DestEnum.DA;

        case SourceEnum.SAB:
            return DestEnum.DA | DestEnum.DB;

        ...

        default:
            throw new Exception ("Cannot map " + source.ToString() + " to DestEnum!");
    }
}

Править: РАЗЪЯСНЕНИЕ

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

Например, это могло быть:

enum SourceEnum
{
    SA = 0x08,
    SB = 0x20,
    SC = 0x10,
    SAB = SA | SB,
    SABC = SA | SB | SC,
}

enum DestEnum
{
    DA = 0x04,
    DB = 0x80,
    DC = 0x01,
    DAB = DA | DB,
}

Править: Подробнее

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

Я был бы совершенно возможен сделать, чтобы SourceToDestMapper функционировал, пытаясь отобразить SA на DC, например...

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

Например: Наличие флага SourceEnum. SABC вызвал бы функцию SourceToDestMapper, три раза приведя к следующему:

  • SourceEnum. SA отображается на DestEnum. DA
  • SourceEnum. SB отобразился на DestEnum. DB
  • SourceEnum. SC отображается на DestEnum. DC

И получающийся DestEnum был бы: DestEnum. DA | DestEnum. DB | DestEnum. DC

6
задан Stécy 3 February 2010 в 16:33
поделиться

6 ответов

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

Обратите внимание, что конструктор FlagMapper принимает пары отдельных флагов, которые сопоставляются друг с другом. Он также может отображать несколько битов в несколько битов, если вы убедитесь, что все сопоставления согласованы. Если все биты в первом элементе пары включены в исходном перечислении, то биты во втором элементе пары будут установлены в целевом перечислении.

Отображение SALL и DALL в настоящее время не работает, потому что в моем конструкторе я не сопоставил биты более высокого порядка. Я не делал это сопоставление, потому что оно как бы несовместимо с требованием сбоя сопоставления SD.

using System;
using System.Collections.Generic;

namespace Flags
{
    [Flags]
    enum SourceEnum
    {
        SNone = 0x00,

        SA = 0x01,
        SB = 0x02,
        SC = 0x04,
        SD = 0x08,

        SAB = SA | SB,

        SALL = -1,
    }

    [Flags]
    enum DestEnum
    {
        DNone = 0x00,

        DA = 0x01,
        DB = 0x02,
        DC = 0x04,

        DALL = 0xFF,
    }

    class FlagMapper
    {
        protected Dictionary<int, int> mForwardMapping;

        protected FlagMapper(Dictionary<int, int> mappings)
        {
            this.mForwardMapping = mappings;
        }

        protected int Map(int a)
        {
            int result = 0;

            foreach (KeyValuePair<int, int> mapping in this.mForwardMapping)
            {
                if ((a & mapping.Key) == mapping.Key)
                {
                    if (mapping.Value < 0)
                    {
                        throw new Exception("Cannot map");
                    }

                    result |= mapping.Value;
                }
            }

            return result;
        }
    }

    class SourceDestMapper : FlagMapper
    {
        public SourceDestMapper()
            : base(new Dictionary<int, int>
            {
                { (int)SourceEnum.SA, (int)DestEnum.DA },
                { (int)SourceEnum.SB, (int)DestEnum.DB },
                { (int)SourceEnum.SC, (int)DestEnum.DC },
                { (int)SourceEnum.SD, -1 }
            })
        {
        }

        public DestEnum Map(SourceEnum source)
        {
            return (DestEnum)this.Map((int)source);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            SourceDestMapper mapper = new SourceDestMapper();

            Console.WriteLine(mapper.Map(SourceEnum.SA));
            Console.WriteLine(mapper.Map(SourceEnum.SA | SourceEnum.SB));
            Console.WriteLine(mapper.Map(SourceEnum.SAB));

            //Console.WriteLine(mapper.Map(SourceEnum.SALL));

            Console.WriteLine(mapper.Map(SourceEnum.SD));
        }
    }
}
3
ответ дан 17 December 2019 в 02:28
поделиться

Если значения ваших перечислений логически эквивалентны , вы можете просто привести одно перечисление к другому, например

public DestEnum Map(SourceEnum source) {
    return (DestEnum)SourceEnum;
}

В этом случае вы можете просто использовать пару статических классов с членами const int .

Однако, если SourceEnum.SA логически эквивалентен DestEnum.DC или SourceEnum.SAB == DestEnum.SomethingElse , тогда у вас нет выбора, кроме как написать собственное сопоставление.

1
ответ дан 17 December 2019 в 02:28
поделиться

Я сейчас мобильный, поэтому я не могу опубликовать очень подробный ответ, но это может быть стоит читать:

http://sqlite.org/cvstrac/wiki?p=SpeedComparison

Как вы видите SQLite 3 выполняет INSERT намного быстрее при использовании индексов и/или транзакций. Кроме того, команды INSERT FROM, по-видимому, не являются сильными в SQLite.

-121--1679968-

Если требуется сопоставление на основе правил, необходимо использовать regex

$string = "REGISTER 11223344 here";
preg_match("/(\d+)/", $string, $match);
$number = $match[1];

Это будет соответствовать первому набору чисел, поэтому, если вам нужно быть более конкретным, попробуйте:

$string = "REGISTER 11223344 here";
preg_match("/REGISTER (\d+) here/", $string, $match);
$number = $match[1];
-121--3470578-

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

Если можно с уверенностью предположить, что числовые значения "эквивалентных" значений перечисления всегда одинаковы, то реальный вопрос: "имеет ли предоставленное значение набор" флагов ", которые не являются частью целевого перечисления". Одним из способов достижения этой цели является циклический перебор всех возможных значений для целевого перечисления. Если flas установлен, то xor его из значения. Если значение! = 0 в конце цикла, оно не может быть преобразовано.

Если его можно преобразовать, просто приведите значение к int, а затем к новому типу.

PS. Упоминал ли я, что странно делать это в первую очередь?

0
ответ дан 17 December 2019 в 02:28
поделиться
  public ReturnEnum ConvertEnum<InEnum, ReturnEnum>(InEnum fromEnum)
  {
     ReturnEnum ret = (ReturnEnum)Enum.ToObject(typeof(ReturnEnum), fromEnum);
     if (Enum.IsDefined(typeof(ReturnEnum), ret))
     {
        return ret;
     }
     else
     {
        throw new Exception("Nope"); // TODO: Create more informative error here
     }
  }
0
ответ дан 17 December 2019 в 02:28
поделиться

В C auto - ключевое слово, указывающее, что переменная является локальной для блока. Так как это значение по умолчанию для переменных с областью действия блока, оно не нужно и очень редко используется (я не думаю, что когда-либо видел его использование вне примеров в текстах, в которых обсуждается ключевое слово). Я был бы заинтересован, если бы кто-то мог указать случай, когда использование auto требовалось для получения правильного анализа или поведения.

Однако в стандарте C++ 11 ключевое слово auto было «захвачено» для поддержки вывода типа, где тип переменной может быть взят из типа ее инициализатора:

auto someVariable = 1.5;   // someVariable will have type double

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

-121--784168-

Можно просмотреть инфраструктуру Common.Logging . Это фасад вокруг рамок лесозаготовок, таких как log4net и LAB.

-121--4244673-

Общий словарь реализован в виде хэш-таблицы, поэтому сложность алгоритма - O (1). Так что если перечисление довольно большое, это наиболее быстрый путь.

ОТРЕДАКТИРОВАНО: Чтобы прояснить... Предположим, что у вас есть несколько делегатов, которые объявляют правило преобразования, где ОДИН из них по умолчанию (SA- > DA), давайте назовем: default_delegate.

class MyMapper
{
    delegate DestEnum singlemap(SourceEnum);
    static Dictionary<SourceEnum, singlemap> _source2dest = 
       new Dictionary<SourceEnum, singlemap>();
    static MyMapper()
    {
         //place there non-default conversion
         _source2dest[S_xxxx] = My_delegate_to_cast_S_xxxx;
         ......
    }
    static singlemap MapDelegate(SourceEnum se)
    {
        singlemap retval;
        //checking has complexity O(1)
        if(_source2dest.TryGetValue ( se, out retval) )
            return retval;
        return default_delegate;
    }

Поэтому при вызове MyMapper.MapDelegate возвращает любое время удаления для делать сопоставления.

-1
ответ дан 17 December 2019 в 02:28
поделиться

Я думаю, что что-то в этом роде сработает, если предположить, что имена перечислений следуют аналогичному шаблону:

public D Map<D, S>(S enumValue, D defaultValue)
    {

        D se = defaultValue; 
        string n = Enum.GetName(typeof(S), enumValue);

        string[] s = Enum.GetNames(typeof(S));
        string[] d = Enum.GetNames(typeof(D));
        foreach (var v in d)
        {
            if (n.Substring(1, n.Length - 1) == v.Substring(1, v.Length - 1))
            {
                se = (D)Enum.Parse(typeof(D), v);
                break;
            }
        }
        return se;
    }

Варианты 2 - настроить словарь целых чисел для сопоставления ..

DestEnum de = DestEnum.DNone;
        SourceEnum se = SourceEnum.SA;
        Dictionary<int, int> maps = new Dictionary<int, int>();
        maps.Add((int)SourceEnum.SNone, (int)DestEnum.DNone);
        maps.Add((int)SourceEnum.SAB, (int)(DestEnum.DA | DestEnum.DB));
        maps.Add((int)SourceEnum.SA, (int)DestEnum.DA);
        de = (DestEnum)maps[(int)se];
1
ответ дан 17 December 2019 в 02:28
поделиться
Другие вопросы по тегам:

Похожие вопросы: