Depende de lo que desea lograr.
1) si simplemente está tratando de limpiar su código, y quitar la comprobación de tipos repetitivo, entonces lo que quiere hacer es centralizar sus cheques en un método, 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 lo haría como algo más general para tipos básicos, puedes probar algo como Thomas Levesquesuggests - aunque, en verdad, no lo he intentado yo mismo, no estoy familiarizado con las extensiones [recientes?] de Convert
. También una muy buena sugerencia.
3) de hecho, es probable que desee fusionar tanto el 1) como el 2) anteriores en una única extensión que le permita admitir la conversión de valores básicos y la compatibilidad con tipos complejos explícitos.
4) si quiere ser completamente "manos libres", entonces también podría usar de forma predeterminada la deserialización simple antigua [Xml o Binary, cualquiera/o]. Por supuesto, esto restringe su entrada, es decir, todas las entradas deben estar en un formato Xml o Binario apropiado. Honestamente, esto es probablemente excesivo, pero vale la pena mencionarlo.
Por supuesto, todos estos métodos hacen esencialmente lo mismo. No hay magia en ninguno de ellos, en algún momento alguien está realizando una búsqueda lineal [ya sea una búsqueda implícita a través de cláusulas si consecutivas o bajo el capó a través de las instalaciones de conversión y serialización de .Net].
5) si quiere mejorar el rendimiento, entonces lo que quiere hacer es mejorar la parte de "búsqueda" de su proceso de conversión. Cree una lista explícita de "tipos soportados", cada tipo corresponde a un índice en una matriz. En lugar de especificar el Tipo en una llamada, usted especifica el índice.
EDIT: Así, mientras que la búsqueda lineal es clara y rápida, también se me ocurre que sería aún más rápido si el consumidor simplemente obtuviera funciones de conversión y las invocara directamente. Es decir, el consumidor sabe de qué tipo le gustaría convertir a [este es un hecho], por lo que si se tiene que convertir muchos artículos al mismo tiempo,
// 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;
}
}
y sería utilizado como
// 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));
Prefiero este último enfoque, porque le da completo control sobre la conversión. Si usa un contenedor de Inversión de Control [IoC] como Castle Windsor o Unity, entonces la inyección de este servicio se realiza para usted. Además, como está basado en instancia, puede tener varias instancias, cada una con su propio conjunto de reglas de conversión; por ejemplo, si tiene varios controles de usuario, cada uno generando su propio DateTime
u otro formato de cadena complejo.
Diablos, incluso si usted quisiera soportar múltiples reglas de conversión para un solo tipo de destino, eso también es posible, simplemente tiene que extender los parámetros del método para especificar cuál.
No se muestra cómo se define 'p', typo? – Hogan
Como mínimo, debe usar el operador 'is'. He actualizado tu pregunta para verificar correctamente el tipo sin usar el reflejo. –
@David Pfeffer, el operador 'is' se aplica incorrectamente. 'is' en este contexto nunca volverá verdadero [' propType' siempre será de tipo 'Type']. Desea utilizar 'propType == typeof (DateTime)' –