Sederhanakan Metode Generik
Saya menulis GenericDeserializer untuk Apache Kafka. Kelas saya mengimplementasikan IDeserializer<T>dari Confluent.Kafka.Netpaket. Saya perlu menyediakan metode Deserialize yang memiliki tanda tangan ini T Deserialize(ReadOnlySpan<byte> data, bool isNull, SerializationContext context),. Tetapi saya juga perlu menggunakan Deserializerskelas Confluent karena mengimplementasikan beberapa detail tingkat rendah seperti mendekode pesan endian besar dari jaringan sebagai tipe primitif, sebagai array byte, sebagai string UTF8, dll. Bagaimana cara menyederhanakan metode ini. Salah satu contoh penyederhanaan mungkin menghapus penggunaan cast yang saya perkenalkan untuk membuat compiler senang.
Logika saya dalam metode ini adalah seperti ini, gunakan setiap jenis yang didukung di Deserializerskelas yang sudah diterapkan . Untuk tipe lain yang tidak diserialisasi dengan bantuan kelas ini, gunakan Json Serialization. Ini kode saya:
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
});
}
}
Berikut adalah sumber Deserializerskelas dariConfluent.Kafka https://github.com/confluentinc/confluent-kafka-dotnet/blob/master/src/Confluent.Kafka/Deserializers.cs
Jawaban
Anda dapat menentukan pemetaan antara Types dan Deserializers. Anda dapat melakukan ini misalnya seperti ini:
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();
}
Anda tidak dapat menggunakan IDeserializer<T>dalam parameter tipe Nilai Kamus itu sebabnya ini adalah object.
Kemudian yang perlu Anda lakukan adalah membuat panggilan pencarian dan mencoba memasukkan nilainya ke IDeserializer<T>
if (_deserializers.ContainsKey(typeof(T)))
{
var deserializer = _deserializers[typeof(T)] as IDeserializer<T>;
...
}
Jika tipe sudah ditemukan maka Anda bisa membuat percabangan berdasarkan isNullnilai:
if (_deserializers.ContainsKey(typeof(T)))
{
var deserializer = _deserializers[typeof(T)] as IDeserializer<T>;
var retVal = deserializer.Deserialize(data, isNull, context);
return !isNull ? retVal : default;
}
Jika tidak ditemukan, Anda dapat menggunakan JsonSerializersebagai pengganti Anda.
return JsonSerializer.Deserialize<T>(data, new JsonSerializerOptions()
{
PropertyNameCaseInsensitive = true
});
Kode terakhir akan terlihat seperti ini:
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
});
}
}
Anda dapat menyingkirkan percabangan dalam Deserializemetode dan sebagai gantinya memindahkan logika tersebut ke tempat yang hanya dijalankan sekali - tempat konstruksi. Sebuah instance dari Anda GenericDeserializer<T>akan selalu mengeksekusi cabang yang sama untuk input apa pun, karena instance tersebut sudah terikat dengan tipe output spesifik T. Untuk tipe seperti int, dan double di mana Anda menggunakan implementasinya, kembalikan implementasinya, gunakan implementasi Anda untuk beristirahat.
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>();
}