#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, который обрабатывает все типы сообщений» без упаковки. Вся обработка должна быть «только для пересылки», при этом метод, который десериализуется в структуру, вызывает метод, который ее обрабатывает.