类型转换- c#将字符串数据表示转换为类型化



现在我正在编写一个从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和两种enum类型作为t。
在这种情况下我应该怎么做?我应该如何将字符串值转换为T类型?
提前感谢!

您可以使用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;
}

我会使用TypeConverter之类的东西。它基本上是一个在价值观和文化之间进行转换的类。TypeConverter和Convert之间的主要区别。ChangeType是后者需要源类型上的IConvertible接口,而TypeConverters可以处理任何对象。

我为此创建了一个helper类,因为我经常在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)支持从包含版本号的字符串("a.b.c.d")到实际的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)。这需要在TypeDescriptorProvider中返回一个自定义的CustomTypeDescriptor。GetConverter方法,并且描述符需要重写GetConverter以返回一个新的VersionTypeConverter实例。一件容易的事。div;)

如果T是您自己定义的类型,则内置方法将不起作用。假设xml看起来像:

//some other segments
<Book Name="Good book" Price="20" Author="Jack" />

而你T是类Book,它看起来像:

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
}

我认为你应该在你的代码中做以下检查:

if (attrib.Value is T)
{
...
}

最新更新