C#将字符串数据表示转换为键入

Ant*_*nov 1 c# type-conversion

现在我正在编写一个从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);
        }
    }
Run Code Online (Sandbox Code Playgroud)

此方法应尝试读取具有指定名称的属性,如果此属性错过,则应返回属性类型的默认值.属性类型由T指定.如上面的注释所示,我的问题是我无法将字符串值普遍转换为特定类型.实际上我计划使用int,double和两个enum类型作为T.
我应该在这种情况下采取什么行动?我应该如何将字符串值转换为T类型?
提前致谢!

Joh*_*ren 8

你可以使用Convert.ChangeType.它基本上是你想要的.但这不是演员,也不是演员.

return (T)Convert.ChangeType(attrib.Value, typeof(T), CultureInfo.InvariantCulture);
Run Code Online (Sandbox Code Playgroud)

您可以简单地将字符串转换为某种任意类型的原因是类型系统不允许这样做.但是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;
}
Run Code Online (Sandbox Code Playgroud)


sis*_*sve 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));
    }
}
Run Code Online (Sandbox Code Playgroud)

完全可以创建自己的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);
    }
}
Run Code Online (Sandbox Code Playgroud)

要实际使用此提供程序,您需要在应用程序启动期间使用TypeDescriptor.AddProvider注册它,传入自定义TypeDescriptionProvider,以及typeof(Version).这需要在TypeDescriptorProvider.GetTypeDescriptor方法中返回自定义CustomTypeDescriptor,并且描述符需要覆盖GetConverter以返回VersionTypeConverter的新实例.简单.;)