В зависимости от того, что вы хотели бы достичь.
1) если вы просто пытаетесь очистить код и удалить повторяющиеся проверки типов, то, что вы хотите сделать, это централизовать свои чеки в методе, 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), если бы как что-то более обобщенное для базовых типов, вы можете попробовать что-то вроде Thomas Levesquesuggests - хотя, по правде говоря, я этого не делал сам, я не знаком с [последними?] расширениями до Convert
. Также очень хорошее предложение.
3) на самом деле вы, вероятно, захотите объединить и 1) и 2) выше в одно расширение, которое позволит вам поддерживать базовое преобразование значений и явную поддержку сложного типа.
4) Если вы хотите быть полностью «свободным от рук», вы также можете по умолчанию использовать обычную старую десериализацию [Xml или Binary, либо/или]. Конечно, это ограничивает ваш вход - то есть все входные данные должны быть в формате Xml или Binary. Честно говоря, это, вероятно, слишком много, но стоит упомянуть.
Конечно, все эти методы делают практически то же самое. В любом из них нет магии, в какой-то момент кто-то выполняет линейный поиск [является ли он неявным просмотром через последовательные if-предложения или под капотом через средства преобразования и преобразования .Net).
5) если вы хотите повысить производительность, то то, что вы хотите сделать, - это улучшить «поиск» части вашего процесса преобразования. Создайте явный список «поддерживаемых типов», каждый из которых соответствует индексу в массиве. Вместо указания типа при вызове вы указываете индекс.
EDIT: поэтому, в то время как линейный поиск является аккуратным и быстрым, это также имеет место для меня, было бы еще быстрее, если бы потребитель просто получал функции преобразования и вызывал их напрямую. То есть, потребитель знает, какого типа он хотел бы, чтобы преобразовать в [это данность], поэтому, если необходимо преобразовать много элементов в одно время,
// 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;
}
}
и будет использоваться в качестве
// 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));
Я предпочитаю этот последний подход, потому что он дает вам полный контроль над преобразованием. Если вы используете контейнер Inversion of Control [IoC], такой как Castle Windsor или Unity, тогда для вас выполняется инъекция этой услуги. Кроме того, поскольку на основе экземпляра вы можете иметь несколько экземпляров, каждый со своим набором правил преобразования - если, например, у вас есть несколько пользовательских элементов управления, каждый из которых генерирует собственный DateTime
или другой сложный строковый формат.
Если вы хотите поддерживать несколько правил преобразования для одного целевого типа, это также возможно, вам просто нужно расширить параметры метода, чтобы указать, какой из них.
Вы не показываете, как определяется 'p', опечатка? – Hogan
По крайней мере, вы должны использовать оператор 'is'. Я обновил ваш вопрос, чтобы правильно проверить тип, не используя рефлексию. –
@David Pfeffer, оператор 'is' неправильно применен. 'is' в этом контексте никогда не вернет true [' propType' всегда будет иметь тип 'Type']. вы хотите использовать 'propType == typeof (DateTime)' –