Il peut parfois s'avérer utile, notamment lorsque l'on souhaite ajouter une propriété à une classe, d'utiliser le downcasting, malheureusement celui-ci n'est pas autorisé en .NET, la compilation sera faite mais une exception de type System.InvalidCastException sera générée à l'exécution. Le mécanisme d'extension de classes ne peut être utilisé ici et si la classe à étendre n'est pas partial, il ne reste plus que le mécanisme de l'héritage. Or parfois l'on se retrouve avec un ensemble d'objets déjà définis, objets que l'on a étendu à l'aide de l'héritage. Il faudrait donc arrivé à effectuer un cast de ces objets ClassBase en ClassDerived
idéalement
ClassDerived derivedObject = (ClassDerived)baseObject;
mais .NET ne le permet pas, on ne peut caster en effet que du sens enfant vers parent
ClassBase baseObject = (ClassBase)derivedObject;
Ceci se comprend aisément puisque effectuer un cast dans le sens parent vers enfant reviendrait à dire que toutes les propriétés ajoutées à l'enfant ne peuvent pas être initialisées avec une valeur provenant du parent puisqu'il n'en a aucune connaissance. Pourtant la création d'un objet dérivé va initialiser les valeurs de ces propriétés à leur valeur par défaut, on peut donc utiliser ce mécanisme pour "caster" l'objet parent en enfant.
En effet la technique va consister à créer un objet enfant puis à recopier toutes les valeurs des propriétés de l'objet parent, qui se trouvent dans l'objet enfant puisque celui-ci hérite de l'objet parent, dans les propriétés de l'objet enfant puis à renvoyer ce nouvel objet créé.
Pour réaliser cela, je vous propose une classe statique helper contenant la fonction de downcasting en générique.
using System.Collections.Generic;
using System.Reflection;
public static class GenericHelper
{
/// <summary>
/// Convertir un objet TFrom en TTo, TTo héritant de TFrom
/// </summary>
/// <example>
/// Appel à la fonction
/// <code>
/// GenericHelper.DownCastingObject<TypeCible, TypeSource>(objetSource)
/// </code>
/// </example>
/// <typeparam name="TTo">
/// <c>Type</c> cible de la conversion
/// </typeparam>
/// <typeparam name="TFrom">
/// <c>Type</c> source de la conversion
/// </typeparam>
/// <param name="data">
/// Objet à convertir
/// </param>
/// <returns>
/// Un nouvel objet TTo
/// </returns>
public static TTo DownCastingObject<TTo, TFrom> (TFrom data) where TTo : TFrom, new()
{
var objet = new TTo();
foreach (PropertyInfo prop in typeof(TFrom).GetProperties())
{
objet.GetType().GetProperty(prop.Name).SetValue(objet, prop.GetValue(data, null), null);
}
return objet;
}
/// <summary>
/// Convertir une List<TFrom> en List<TTo>, TTo héritant de TFrom
/// </summary>
/// <example>
/// Appel à la fonction
/// <code>
/// GenericHelper.ConvertListTo<TypeCible, TypeSource>(listeDonnees)
/// </code>
/// </example>
/// <typeparam name="TTo">
/// <c>Type</c> cible de la conversion
/// </typeparam>
/// <typeparam name="TFrom">
/// <c>Type</c> source de la conversion
/// </typeparam>
/// <param name="datas">
/// Données à convertir
/// </param>
/// <returns>
/// Une nouvelle List<TTo>
/// </returns>
public static List<TTo> ConvertListTo<TTo, TFrom>(List<TFrom> datas) where TTo : TFrom, new()
{
var list = new List<TTo>();
if(typeof(TTo).BaseType == typeof(TFrom))
{
foreach (var data in datas)
{
var o = (TTo)DownCastingObject<TTo, TFrom>(data);
list.Add(o);
}
}
return list;
}
}
Le downcasting s’effectue donc de la façon suivante
var baseObject = new ClassBase
{
Libelle = "Classe de base"
};
var derivedObject = GenericHelper.DownCastingObject<ClassDerived, ClassBase>(baseObject);
Le helper contient également une fonction de conversion d’une List<> d’objets de base en une List<> d’objets dérivés, à utiliser de la façon suivante
var listDerived = GenericHelper.ConvertListTo<ClassDerived, ClassBase>(listBase);
L'ensemble des sources est à retrouver sur mon skydrive