现在我正在编写一个从XML加载配置数据的通用方法。我的案例中有很多参数都存储在节点属性中,所以我决定编写一个通用的属性读取方法:
private static T ReadAttribute<T>(XElement Element,string AttributeName)
{
var attrib = Element.Attribute(AttributeName);
if (attrib != null)
{
return attrib.Value; // off cource error is in this line !
}
else
{
return default(T);
}
}
此方法应尝试读取具有指定名称的属性,如果此属性错过,则应返回属性类型的默认值。属性类型由T指定。
正如上面评论中所示,我的问题是我无法将字符串值普遍转换为特定类型。实际上我计划使用int,double和两个枚举类型作为T.
在这种情况下我应该采取什么行动?我应该如何将字符串值转换为T类型?
提前谢谢!
答案 0 :(得分:8)
您可以使用Convert.ChangeType
。它基本上是你想要的。但这不是演员,也不仅仅是演员。
return (T)Convert.ChangeType(attrib.Value, typeof(T), CultureInfo.InvariantCulture);
您可以简单地将字符串转换为某种任意类型的原因是类型系统不允许这样做。但是Convert.ChangeType
返回的对象可以是任何类型,因此允许转换。
CultureInfo.InvariantCulture
很重要,因为XML内容不应该使用不同的文化进行编码/解码。如果使用XML,则应使用XmlConvert类,但它没有像XmlConvert.ChangeType
这样的方便的通用方法。
XAttribute
类有许多显式的用户定义的强制转换,映射到XmlConvert类。但是,您不能简单地将这些与无约束类型参数T一起使用,并期望得到相同的结果。
更糟糕的是,XML和Convert实际上并不好看。所以,如果你真的认真对待这个问题,你会写这样的东西来处理转换。
static T ConvertTo<T>(XAttribute attr)
{
object value;
switch (Type.GetTypeCode(typeof(T)))
{
case TypeCode.Boolean: value = XmlConvert.ToBoolean(attr.Value); break;
case TypeCode.Int32: value = XmlConvert.ToInt32(attr.Value); break;
case TypeCode.DateTime: value = XmlConvert.ToDateTime(attr.Value); break;
// Add support for additional TypeCode values here...
default:
throw new ArgumentException(string.Format("Unsupported destination type '{0}'.", typeof(T)));
}
return (T)value;
}
答案 1 :(得分:5)
我会选择TypeConverter的东西。它基本上是一个与价值观和文化进行转换的类。 TypeConverter和Convert.ChangeType之间的主要区别在于,后者需要源类型上的IConvertible接口,而TypeConverters可以使用任何对象。
我为此创建了一个帮助器类,因为我经常在xml文件中存储不同的配置对象。这也是为什么它被转换为/来自CultureInfo.InvariantCulture的硬编码。
public static class TypeConversion {
public static Object Convert(Object source, Type targetType) {
var sourceType = source.GetType();
if (targetType.IsAssignableFrom(sourceType))
return source;
var sourceConverter = TypeDescriptor.GetConverter(source);
if (sourceConverter.CanConvertTo(targetType))
return sourceConverter.ConvertTo(null, CultureInfo.InvariantCulture, source, targetType);
var targetConverter = TypeDescriptor.GetConverter(targetType);
if (targetConverter.CanConvertFrom(sourceType))
return targetConverter.ConvertFrom(null, CultureInfo.InvariantCulture, source);
throw new ArgumentException("Neither the source nor the target has a TypeConverter that supports the requested conversion.");
}
public static TTarget Convert<TTarget>(object source) {
return (TTarget)Convert(source, typeof(TTarget));
}
}
完全可以创建自己的TypeConverter来处理系统类型,例如System.Version(它不实现IConvertible),以支持从包含版本号(“abcd”)的字符串到实际Version对象的转换。
public class VersionTypeConverter : TypeConverter {
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) {
if (sourceType == typeof(string))
return true;
return base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) {
var s = value as string;
if (s != null)
return new Version(s);
return base.ConvertFrom(context, culture, value);
}
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) {
if (destinationType == typeof(string))
return true;
return base.CanConvertTo(context, destinationType);
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) {
var v = value as Version;
if (v != null && destinationType == typeof(string)) {
return v.ToString();
}
return base.ConvertTo(context, culture, value, destinationType);
}
}
要实际使用此提供程序,您需要在应用程序启动期间使用TypeDescriptor.AddProvider,传入自定义TypeDescriptionProvider和typeof(Version)
进行注册。这需要在CustomTypeDescriptor方法中返回自定义TypeDescriptorProvider.GetTypeDescriptor,并且描述符需要覆盖GetConverter以返回VersionTypeConverter的新实例。简单。 ;)
答案 2 :(得分:1)
如果T是您自己定义的类型,内置方法将无济于事。让我们说xml看起来像:
//some other segments
<Book Name="Good book" Price="20" Author="Jack" />
你T是看起来像的课本:
class Book
{
public string Name { get; set; }
public decimal Price { get; set; }
public string Author { get; set; }
//maybe some other properties
}
将XElement
自动转换为Book
的实例是没有神奇的,您需要自己实现它。一个简单而通用的实现是这样的:
interface IXElementConvertible
{
void LoadFrom(XElement element);
}
class Book : IXElementConvertible
{
public string Name { get; set; }
public decimal Price { get; set; }
public string Author { get; set; }
public void LoadFrom(XElement element)
{
this.Name = element.Attribute("Name").Value;
//blabla
}
}
您需要修改方法:
private static T ReadAttribute<T>(XElement Element,string AttributeName)
where T : IXElementConvertible, new()
{
T t = new T();
t.LoadFrom(element);
//just an example here, not the complete implementation
}
答案 3 :(得分:0)
我认为您应该在代码中执行以下检查:
if (attrib.Value is T)
{
...
}