Simplifier la méthode générique
J'écris un GenericDeserializer pour Apache Kafka. Ma classe implémente à IDeserializer<T>partir du Confluent.Kafka.Netpackage. Je dois fournir une méthode Deserialize qui a cette signature, T Deserialize(ReadOnlySpan<byte> data, bool isNull, SerializationContext context). Mais j'ai également besoin d'utiliser la Deserializersclasse de Confluent car elle implémente des détails de bas niveau tels que le décodage d'un message big endian du réseau en tant que types primitifs, en tant que tableau d'octets, en tant que chaîne UTF8, etc. Un exemple de simplification peut être la suppression de l'utilisation des transtypages que j'ai introduits pour rendre le compilateur heureux.
Ma logique dans cette méthode est la suivante, utilisez tous les types pris en charge dans la Deserializersclasse déjà implémentée . Pour les autres types qui ne sont pas désérialisés à l'aide de cette classe, utilisez la sérialisation Json. Voici mon code:
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
});
}
}
Voici la source de la Deserializersclasse deConfluent.Kafka https://github.com/confluentinc/confluent-kafka-dotnet/blob/master/src/Confluent.Kafka/Deserializers.cs
Réponses
Vous pouvez définir un mappage entre Types et le Deserializers. Vous pouvez le faire par exemple comme ceci:
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();
}
Vous ne pouvez pas utiliser le IDeserializer<T>paramètre de type Valeur du dictionnaire, c'est pourquoi il s'agit d'un object.
Ensuite, tout ce que vous avez à faire est de faire un appel de recherche et d'essayer de convertir la valeur en IDeserializer<T>
if (_deserializers.ContainsKey(typeof(T)))
{
var deserializer = _deserializers[typeof(T)] as IDeserializer<T>;
...
}
Si le type a été trouvé, vous pouvez créer des branches en fonction de la isNullvaleur:
if (_deserializers.ContainsKey(typeof(T)))
{
var deserializer = _deserializers[typeof(T)] as IDeserializer<T>;
var retVal = deserializer.Deserialize(data, isNull, context);
return !isNull ? retVal : default;
}
S'il n'est pas trouvé, vous pouvez utiliser le JsonSerializercomme solution de secours.
return JsonSerializer.Deserialize<T>(data, new JsonSerializerOptions()
{
PropertyNameCaseInsensitive = true
});
Le code final ressemblerait à ceci:
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
});
}
}
Vous pouvez vous débarrasser du branchement dans la Deserializeméthode et déplacer à la place cette logique vers un endroit qui n'est exécuté qu'une seule fois - le lieu de construction. Une instance de your GenericDeserializer<T>exécutera toujours la même branche pour n'importe quelle entrée, car l'instance est déjà liée à un type de sortie spécifique T.Pour les types comme int, et double où vous utilisez leur implémentation retournent simplement leur implémentation, utilisez votre implémentation pour le du repos.
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>();
}