C # type générique dans une classe de base
-
18-09-2019 - |
Question
J'écris un système qui a un ensemble de tampons de protocole (en utilisant protobuf-net), je veux définir quelque chose comme ça dans une classe abstraite tous hériteront de:
public byte[] GetBytes()
cependant, le serealiser tampon de protocole nécessite un argument de type, est-il un moyen efficace pour obtenir le type de la classe héritant?
Exemple:
public byte[] GetBytes()
{
using (MemoryStream stream = new MemoryStream())
{
Serializer.Serialize<T /* what goes here? */>(stream, this);
return stream.ToArray();
}
}
La solution
Il suffit d'écrire "T" droit?
puis dans votre déclaration de classe:
public class M<T>
- Modifier
Et puis quand vous héritez:
public class Foo : M<Apple>
Autres conseils
Vous pouvez le faire via la réflexion, mais protobuf-net fait pour vous.
Il suffit de changer votre appel à:
Serializer.NonGeneric.Serialize(stream, this /* Takes an object here */);
Cela fonctionne en construisant la méthode générique lors de l'exécution par réflexion. Pour plus de détails, consultez le code ( seconde méthode ).
Définissez votre classe de base comme BaseClass<T>
et vos classes dérivées remplacent T avec le DerivedClass<SerializerType>
type sérialiseur.
Vous pouvez également spécifier des contraintes sur l'argument de par exemple de type.
BaseClass<T> where T : SerializerBase
est une description de les types de contraintes que vous pouvez appliquer.
Vous n'avez pas fait quoi que ce soit de spécial ... puisque l'héritage respecte la protobuf-net. Si vous avez:
[ProtoInclude(typeof(Foo), 20)]
[ProtoInclude(typeof(Bar), 21)]
public abstract class MyBase {
/* other members */
public byte[] GetBytes()
{
using(MemoryStream ms = new MemoryStream())
{
Serializer.Serialize<MyBase>(ms, this); // MyBase can be implicit
return ms.ToArray();
}
}
}
[ProtoContract]
class Foo : MyBase { /* snip */ }
[ProtoContract]
class Bar : MyBase { /* snip */ }
alors il fonctionnera. Pour sérialiser les données, il commence toujours à la base (contrat) de type ; même si vous avez fait Serializer.Serialize<Foo>(stream, obj)
la première chose qu'il va faire est de détecter qu'il a une classe de base qui est un contrat, et passer à MyBase
. Au cours de désérialisation il identifiera la bonne dérivée (béton) type et l'utiliser, vous pouvez donc utiliser Deserialize
avec MyBase
aussi, et il construira un Foo
ou Bar
en fonction de ce que les données d'origine était.
Ainsi, les éléments suivants sont en grande partie identiques:
Serializer.Serialize<BaseType>(dest, obj);
...
BaseType obj = Serializer.Deserialize<BaseType>(source);
et
Serializer.Serialize<DerivedType>(dest, obj);
...
DerivedType obj = Serializer.Deserialize<DerivedType>(source);
La principale différence est de savoir comment les variables sont typés.