Зависит от того, чего вы хотите добиться.
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 Levesque предлагает - хотя, по правде говоря, я сам этого не пробовал, я незнаком с [последними?] расширениями до 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
или другой формат сложной строки. .
Черт возьми, даже если вы хотите поддерживать несколько правил преобразования для одного целевого типа, это также возможно, вам просто нужно расширить параметры метода, чтобы указать, какой из них.
person
johnny g
schedule
04.03.2010
p
, опечатка? - person Hogan   schedule 04.03.2010is
. Я обновил ваш вопрос, чтобы правильно проверить тип без использования отражения. - person David Pfeffer   schedule 04.03.2010is
применяется неправильно.is
в этом контексте никогда не вернет true [propType
всегда будет иметь типType
]. вы хотите использоватьpropType == typeof(DateTime)
- person johnny g   schedule 04.03.2010typeof
, но я потратил свою репутацию на вопрос о вознаграждении :P - person johnny g   schedule 04.03.2010