Cela dépend de ce que vous souhaitez accomplir.
1) si vous essayez simplement de nettoyer votre code, et de supprimer les vérifications de type répétitives, alors ce que vous voulez faire, c'est centraliser vos vérifications dans une méthode, comme
public static T To<T> (this string stringValue)
{
T value = default (T);
if (typeof (T) == typeof (DateTime))
{
// insert custom or convention System.DateTime
// deserialization here ...
}
// ... add other explicit support here
else
{
throw new NotSupportedException (
string.Format (
"Cannot convert type [{0}] with value [{1}] to type [{2}]." +
" [{2}] is not supported.",
stringValue.GetType (),
stringValue,
typeof (T)));
}
return value;
}
2) si vous souhaitez quelque chose de plus généralisé pour les types de base, vous pouvez essayer quelque chose comme Thomas Levesque suggère - bien qu'en vérité, je n'aie pas tenté de le faire moi-même, je ne suis pas familier avec les extensions [récentes ? Convert
. C'est aussi une très bonne suggestion.
3) en fait, vous souhaitez probablement fusionner les deux points 1) et 2) ci-dessus en une seule extension qui vous permettrait de prendre en charge la conversion des valeurs de base et le support explicite des types complexes.
4) si vous voulez être complètement "mains libres", vous pouvez aussi choisir par défaut la bonne vieille désérialisation [Xml ou binaire, au choix]. Bien sûr, cela contraint vos entrées - c'est-à-dire que toutes les entrées doivent être dans un format Xml ou Binaire approprié. Honnêtement, c'est probablement exagéré, mais cela vaut la peine d'être mentionné.
Bien sûr, toutes ces méthodes font essentiellement la même chose. Il n'y a pas de magie dans aucune d'entre elles, à un moment donné quelqu'un effectue une recherche linéaire [qu'il s'agisse d'une recherche implicite par le biais de clauses if séquentielles ou d'une recherche sous le capot via les fonctions de conversion et de sérialisation de .Net].
5) si vous voulez améliorer les performances, vous devez améliorer la partie "recherche" de votre processus de conversion. Créez une liste explicite de "types supportés", chaque type correspondant à un index dans un tableau. Au lieu de spécifier le type lors d'un appel, vous spécifiez alors l'index.
EDIT : Ainsi, bien que la recherche linéaire soit pratique et rapide, il me semble que ce serait encore plus rapide si le consommateur obtenait simplement les fonctions de conversion et les invoquait directement. En d'autres termes, le consommateur sait quel type il souhaite convertir [c'est une donnée], donc s'il a besoin de convertir de nombreux éléments à la fois,
// S == source type
// T == target type
public interface IConvert<S>
{
// consumers\infrastructure may now add support
int AddConversion<T> (Func<S, T> conversion);
// gets conversion method for local consumption
Func<S, T> GetConversion<T> ();
// easy to use, linear look up for one-off conversions
T To<T> (S value);
}
public class Convert<S> : IConvert<S>
{
private class ConversionRule
{
public Type SupportedType { get; set; }
public Func<S, object> Conversion { get; set; }
}
private readonly List<ConversionRule> _map = new List<ConversionRule> ();
private readonly object _syncRoot = new object ();
public void AddConversion<T> (Func<S, T> conversion)
{
lock (_syncRoot)
{
if (_map.Any (c => c.SupportedType.Equals (typeof (T))))
{
throw new ArgumentException (
string.Format (
"Conversion from [{0}] to [{1}] already exists. " +
"Cannot add new conversion.",
typeof (S),
typeof (T)));
}
ConversionRule conversionRule = new ConversionRule
{
SupportedType = typeof(T),
Conversion = (s) => conversion (s),
};
_map.Add (conversionRule);
}
}
public Func<S, T> GetConversion<T> ()
{
Func<S, T> conversionMethod = null;
lock (_syncRoot)
{
ConversionRule conversion = _map.
SingleOrDefault (c => c.SupportedType.Equals (typeof (T)));
if (conversion == null)
{
throw new NotSupportedException (
string.Format (
"Conversion from [{0}] to [{1}] is not supported. " +
"Cannot get conversion.",
typeof (S),
typeof (T)));
}
conversionMethod =
(value) => ConvertWrap<T> (conversion.Conversion, value);
}
return conversionMethod;
}
public T To<T> (S value)
{
Func<S, T> conversion = GetConversion<T> ();
T typedValue = conversion (value);
return typedValue;
}
// private methods
private T ConvertWrap<T> (Func<S, object> conversion, S value)
{
object untypedValue = null;
try
{
untypedValue = conversion (value);
}
catch (Exception exception)
{
throw new ArgumentException (
string.Format (
"Unexpected exception encountered during conversion. " +
"Cannot convert [{0}] [{1}] to [{2}].",
typeof (S),
value,
typeof (T)),
exception);
}
if (!(untypedValue is T))
{
throw new InvalidCastException (
string.Format (
"Converted [{0}] [{1}] to [{2}] [{3}], " +
"not of expected type [{4}]. Conversion failed.",
typeof (S),
value,
untypedValue.GetType (),
untypedValue,
typeof (T)));
}
T typedValue = (T)(untypedValue);
return typedValue;
}
}
et il serait utilisé comme
// as part of application innitialization
IConvert<string> stringConverter = container.Resolve<IConvert<string>> ();
stringConverter.AddConversion<int> (s => Convert.ToInt32 (s));
stringConverter.AddConversion<Color> (s => CustomColorParser (s));
...
// a consumer elsewhere in code, say a Command acting on
// string input fields of a form
//
// NOTE: stringConverter could be injected as part of DI
// framework, or obtained directly from IoC container as above
int someCount = stringConverter.To<int> (someCountString);
Func<string, Color> ToColor = stringConverter.GetConversion <Color> ();
IEnumerable<Color> colors = colorStrings.Select (s => ToColor (s));
Je préfère de loin cette dernière approche, car elle vous donne complet le contrôle de la conversion. Si vous utilisez un conteneur d'inversion de contrôle [IoC] comme Castle Windsor ou Unity, l'injection de ce service est effectuée pour vous. De plus, comme il s'agit d'un instance vous pouvez avoir plusieurs instances, chacune avec son propre ensemble de règles de conversion - si, par exemple, vous avez plusieurs contrôles d'utilisateur, chacun générant sa propre règle de conversion DateTime
ou tout autre format de chaîne complexe.
Même si vous souhaitez prendre en charge plusieurs règles de conversion pour un seul type de cible, c'est également possible. Il vous suffit d'étendre les paramètres de la méthode pour spécifier laquelle.
1 votes
Vous ne montrez pas comment
p
est défini, une faute de frappe ?1 votes
Au minimum, vous devriez utiliser l'option
is
opérateur. J'ai mis à jour votre question pour vérifier correctement le type sans utiliser la réflexion.3 votes
@David Pfeffer,
is
est appliqué de manière incorrecte.is
dans ce contexte ne retournera jamais vrai [propType
sera toujours de typeType
]. vous voulez utiliserpropType == typeof(DateTime)
0 votes
J'ai le Type. J'ai laissé de côté le code où j'instancie P. Mais supposons que je l'ai.
0 votes
@johnny g, Oh, vous avez raison, j'ai complètement mal lu le code. Je vais annuler ma modification.
1 votes
Hehe, pas de soucis, je l'ai repéré parce que je l'ai fait tellement de fois moi-même ;) je l'aurais modifié pour l'utiliser.
typeof
aussi, mais j'ai dépensé mon représentant sur une question de prime :P