取决于你想完成什么。
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 - 虽然事实上,我自己并没有尝试过,我不熟悉 [recent?] 对 @ 的扩展987654324@。也是一个很好的建议。
3) 事实上,您可能希望将上面的 1) 和 2) 合并到一个扩展中,使您能够支持基本的值转换和显式的复杂类型支持。
4) 如果您想完全“解放双手”,那么您也可以默认使用普通的旧反序列化 [Xml 或二进制,非此即彼]。当然,这会限制您的输入 - 即所有输入必须采用适当的 Xml 或二进制格式。老实说,这可能有点矫枉过正,但值得一提。
当然,所有这些方法本质上都做同样的事情。它们中的任何一个都没有魔法,在某些时候 someone 正在执行线性查找 [无论是通过顺序 if 子句的隐式查找还是通过 .Net 转换和序列化工具在幕后进行的查找] .
5) 如果您想提高性能,那么您要做的就是改进转换过程中的“查找”部分。创建一个明确的“支持的类型”列表,每种类型对应于数组中的一个索引。然后指定索引,而不是在调用中指定类型。
编辑:因此,虽然线性查找简洁而快速,但我也想到如果消费者简单地获取转换函数并直接调用它们会更快。也就是说,消费者知道它想转换成什么类型[this is a given],所以如果它需要一次转换很多项,
// 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));
我更喜欢后一种方法,因为它让您完全控制转换。如果您使用控制反转 [IoC] 容器(如 Castle Windsor 或 Unity),则此服务的注入已为您完成。此外,因为它是基于 instance 的,所以您可以拥有多个实例,每个实例都有自己的一组转换规则 - 例如,如果您有多个用户控件,每个都生成自己的 DateTime 或其他复杂的字符串格式。
哎呀,即使你想支持单个目标类型的多个转换规则,这也是可能的,你只需要扩展方法参数来指定哪个。