Упростить общий метод
Я пишу GenericDeserializer для Apache Kafka. Мой класс реализует IDeserializer<T>из Confluent.Kafka.Netпакета. Мне нужно поставить метод десериализации , который имеет эту подпись, T Deserialize(ReadOnlySpan<byte> data, bool isNull, SerializationContext context). Но мне также нужно использовать Deserializersкласс Confluent, потому что он реализует некоторые детали низкого уровня, такие как декодирование сообщения с прямым порядком байтов из сети как примитивные типы, как массив байтов, как строка UTF8 и т. Д. Как я могу упростить этот метод. Одним из примеров упрощения может быть отказ от использования приведений, которые я ввел, чтобы сделать компилятор счастливым.
Моя логика в этом методе такова: используйте все поддерживаемые типы в уже реализованном Deserializersклассе. Для других типов, которые не десериализованы с помощью этого класса, используйте сериализацию Json. Вот мой код:
public class GenericDeserializer<T> : IDeserializer<T>
{
public T Deserialize(ReadOnlySpan<byte> data, bool isNull, SerializationContext context)
{
var type = typeof(T);
if (type == typeof(double))
{
var retVal = Deserializers.Double.Deserialize(data, isNull, context);
return (T) (object) retVal;
}
if (type == typeof(float))
{
var retVal = Deserializers.Single.Deserialize(data, isNull, context);
return (T) (object) retVal;
}
if (type == typeof(int))
{
var retVal = Deserializers.Int32.Deserialize(data, isNull, context);
return (T) (object) retVal;
}
if (type == typeof(long))
{
var retVal = Deserializers.Int64.Deserialize(data, isNull, context);
return (T) (object) retVal;
}
if (type == typeof(Null))
{
var retVal = Deserializers.Null.Deserialize(data, isNull, context);
return (T) (object) retVal;
}
if (type == typeof(string))
{
var retVal = Deserializers.Utf8.Deserialize(data, isNull, context);
return (T) (object) retVal;
}
if (type == typeof(byte[]))
{
var retVal = Deserializers.ByteArray.Deserialize(data, isNull, context);
return (T) (object) retVal;
}
if (isNull)
{
return default;
}
return JsonSerializer.Deserialize<T>(data, new JsonSerializerOptions()
{
PropertyNameCaseInsensitive = true
});
}
}
Вот источник Deserializersкласса отConfluent.Kafka https://github.com/confluentinc/confluent-kafka-dotnet/blob/master/src/Confluent.Kafka/Deserializers.cs
Ответы
Вы можете определить соответствие между Types и Deserializers. Сделать это можно, например, так:
public class GenericDeserializer<T> : IDeserializer<T>
{
private readonly ImmutableDictionary<Type, object> _deserializers =
new Dictionary<Type, object>
{
{ typeof(double), Deserializers.Double },
{ typeof(float), Deserializers.Single },
{ typeof(int), Deserializers.Int32 },
{ typeof(long), Deserializers.Int64 },
{ typeof(Null), Deserializers.Null },
{ typeof(string), Deserializers.Utf8 },
{ typeof(byte[]), Deserializers.ByteArray },
}.ToImmutableDictionary();
}
Вы не можете использовать IDeserializer<T>параметр типа Value в словаре, поэтому это файл object.
Затем все, что вам нужно сделать, это выполнить поисковый вызов и попытаться привести значение к IDeserializer<T>
if (_deserializers.ContainsKey(typeof(T)))
{
var deserializer = _deserializers[typeof(T)] as IDeserializer<T>;
...
}
Если тип был найден, вы можете выполнить ветвление по isNullзначению:
if (_deserializers.ContainsKey(typeof(T)))
{
var deserializer = _deserializers[typeof(T)] as IDeserializer<T>;
var retVal = deserializer.Deserialize(data, isNull, context);
return !isNull ? retVal : default;
}
Если он не найден, вы можете использовать его JsonSerializerкак запасной вариант.
return JsonSerializer.Deserialize<T>(data, new JsonSerializerOptions()
{
PropertyNameCaseInsensitive = true
});
Окончательный код будет выглядеть так:
public class GenericDeserializer<T> : IDeserializer<T>
{
private readonly ImmutableDictionary<Type, object> _deserializers =
new Dictionary<Type, object>
{
{ typeof(double), Deserializers.Double },
{ typeof(float), Deserializers.Single },
{ typeof(int), Deserializers.Int32 },
{ typeof(long), Deserializers.Int64 },
{ typeof(Null), Deserializers.Null },
{ typeof(string), Deserializers.Utf8 },
{ typeof(byte[]), Deserializers.ByteArray },
}.ToImmutableDictionary();
public T Deserialize(ReadOnlySpan<byte> data, bool isNull, SerializationContext context)
{
if (_deserializers.ContainsKey(typeof(T)))
{
var deserializer = _deserializers[typeof(T)] as IDeserializer<T>;
var retVal = deserializer.Deserialize(data, isNull, context);
return !isNull ? retVal : default;
}
return JsonSerializer.Deserialize<T>(data, new JsonSerializerOptions()
{
PropertyNameCaseInsensitive = true
});
}
}
Вы можете избавиться от ветвления в Deserializeметоде и вместо этого переместить эту логику в место, которое выполняется только один раз, - в место построения. Ваш экземпляр GenericDeserializer<T>всегда будет выполнять одну и ту же ветвь для любого ввода, потому что экземпляр уже привязан к определенному типу вывода T. Для таких типов, как int и double, где вы используете их реализацию, просто верните их реализацию, используйте вашу реализацию для остальные.
class JsonDeserializer<T> : IDeserializer<T>
{
public T Deserialize(ReadOnlySpan<byte> data, bool isNull, SerializationContext context)
{
if (isNull)
{
return default;
}
return JsonSerializer.Deserialize<T>(data, new JsonSerializerOptions()
{
PropertyNameCaseInsensitive = true
});
}
}
IDeserializer<T> CreateDeserializer<T>()
{
var type = typeof(T);
if (type == typeof(double))
{
return (IDeserializer<T>) Deserializers.Double;
}
if (type === typeof(long))
{
return (IDeserializer<T>) Deserializers.Int64;
}
// ...
return new JsonDeserializer<T>();
}