Возможно ли создать структуру на основе идентификатора (ushort) без бокса и отражения?

#c#

#c#

Вопрос:

У меня есть byte[], где первые два байта являются ushort, который представляет идентификатор, который сообщает мне, какие данные содержит остальная часть массива. На основе этого идентификатора мне нужно создать структуру с соответствующим типом для заполнения полученных данных.

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

Есть ли какое-либо возможное решение без бокса и отражения для создания необходимого structtype на основе идентификатора ushort?

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

 public struct TestMessage
    {
        public const ushort typeCode = (ushort)Enums.MessageOpcodes.TestMessage;    
        public uint testuInt { internal set; get; }
        public ushort testuShort { internal set; get; }
        public ulong testuLong { internal set; get; }

        public TestMessage(uint uInt, ushort uShort, ulong uLong)
        {
            testuInt = uInt;
            testuShort = uShort;
            testuLong = uLong;
        }
    }
  
 public static ReadOnlyDictionary<ushort, object> messageTypes;

private static void PopulateMessageDict()
        {
            var tempMessageTypes = new Dictionary<ushort, object>();

            tempMessageTypes.TryAdd(TestMessage.typeCode, new TestMessage());
            messageTypes = new ReadOnlyDictionary<ushort, object>(tempMessageTypes);
        }

public void TryAdd(this Dictionary<ushort, object> dictionary, ushort key, object value)
        {
            if (!dictionary.ContainsKey(key))
            {
                dictionary.Add(key, value);                
            }
            else
            {
                Debug.Log("Key already exists in dictionary.");
            }
        }
  

Комментарии:

1. Откуда берутся бокс и отражение? Возможно, вам следует поделиться имеющимся у вас кодом. Вероятно, вы можете получить что-то, используя структуры, реализующие интерфейс, и универсальные методы с ограничением типа, использующие этот интерфейс, но трудно сказать, как это должно выглядеть, без более подробной информации о том, чего вы на самом деле пытаетесь достичь.

2. Получите 2 байта, а затем переключите регистр

3. @canton7 Я добавил два фрагмента того, что я пытался начать сейчас. Упаковка в «TryAdd», когда она преобразует структуру в объект, незначительна, поскольку это происходит только при запуске. Моей первой мыслью было, что я мог бы получить тип объекта с помощью TryGetValue и GetType из dict и создать с ним соответствующую структуру. Но после некоторого поиска в Google кажется, что это не сработает хорошо (если вообще сработает), поскольку потребуется отражение для каждого байта [], который я хочу обработать.

4. @Cid это было бы моим максимальным соотношением, но я думал, что смогу как-то это обойти.

5. И как вы хотите использовать messageTypes ? Как вы собираетесь в нее десериализоваться? Планируется ли просмотреть первые 2 байта, использовать это, чтобы определить, в какую структуру десериализовать, вызвать какую-то другую логику для десериализации в нужный вид структуры, а затем передать это … куда-нибудь? Где? Что произойдет после десериализации, что тогда?

Ответ №1:

Итак, основываясь на ваших комментариях, я не думаю, что вам вообще нужен словарь.

Вы сказали, что хотите взять несколько байтов, посмотреть на первые 2, чтобы определить тип сообщения, затем десериализовать в сообщение правильного типа, а затем обработать его способом, специфичным для этого типа.

Итак, на очень высоком уровне то, что вы хотите, это:

 var opCode = (Enums.MessageOpcodes)(data[0] << 8 | data[1]);
switch (opCode)
{
    case Enums.MessageOpcodes.TestMessage:
        ProcessTestMessage(data);
        break;
    case Enums.MessageOpcodes.VectorMessage:
        ProcessVectorMessage(data);
        break;        
}
  

Затем ваш ProcessTestMessage метод десериализуется в TestMessage структуру и затем обрабатывает ее; аналогично вашему ProcessVectorMessage и VectorMessage struct.

Вы можете немного обобщить это:

 interface IMessageProcessor
{
    Enums.MessageOpcodes Opcode { get; }
    void DeserializeAndProcess(byte[] data);
}
  

Тогда:

 class TestMessageProcessor : IMessageProcessor
{
    public Enums.MessageOpcodes Opcode => Enums.MessageOpcodes.TestMessage;
    public void DeserializeAndProcess(byte[] data)
    {
        // Deserialize into a TestMessage
        // Process
    }
}
  

Тогда у вас есть свой Dictionary<Enums.MessageOpcodes, IMessageProcessor> (или на данный момент, поскольку ключ является свойством IMessageProcessor , вы можете использовать KeyedCollection ):

 class MessageProcessorLookup : KeyedCollection<Enums.MessageOpcodes, IMessageProcessor>
{
    public override Enums.MessageOpcodes GetKeyForItem(IMessageProcessor item) => item.Opcode;
}
  

Тогда:

 var lookup = new MessageProcessorLookup() { new TestMessageProcessor(), .... };
...
lookup[opcode].DeserializeAndProcess(data);
  

В конечном счете, если у вас есть куча разных структур, и вы не хотите помещать их в коробку, у вас не может быть кода, в котором говорится «десериализуйте эти данные в соответствующую структуру — мне все равно, какого рода — затем верните их мне», поскольку вам придется поместить структуру в коробку, чтобы вернуть ее. Аналогично, вы не можете сказать «десериализуйте эти данные в соответствующую структуру, а затем отправьте их в этот ConcurrentQueue, который обрабатывает все типы сообщений» без упаковки. Вся обработка должна быть «только для пересылки», при этом метод, который десериализуется в структуру, вызывает метод, который ее обрабатывает.