将对象转换为 T

124

我正在使用.NET中的XmlReader类解析XML文件,我认为编写一个通用的解析函数来通用地读取不同属性是明智的。我想到了以下的函数:

private static T ReadData<T>(XmlReader reader, string value)
{
    reader.MoveToAttribute(value);
    object readData = reader.ReadContentAsObject();
    return (T)readData;
}

我发现,这并不完全按照我的计划工作;它在原始类型(如intdouble)中会抛出错误,因为强制转换不能将string转换为数字类型。有没有办法让我的函数以修改后的形式成功运行?

8个回答

272

首先检查是否可以进行转换。

if (readData is T) {
    return (T)readData;
} 
try {
   return (T)Convert.ChangeType(readData, typeof(T));
} 
catch (InvalidCastException) {
   return default(T);
}

1
我将使用Convert.ChangeType的那一行改为:'return (T)Convert.ChangeType(readData, typeof(T), System.Globalization.CultureInfo.InstalledUICulture.NumberFormat),以使其在各种不同的文化配置中都能正常工作。 - Kasper Holdum
3
这是正确的答案。但我可以提出一个论点,即try/catch在这里完全是多余的。特别是考虑到静音异常。我认为if(readData is T) { ... }部分已经足够了。 - pim
在转换之前,您可以检查readDate是否为null。如果是,则返回default(T)。 - Manuel Koch
3
我收到了“对象必须实现IConvertible”的错误信息。 - Casey Crookston

20

你尝试过使用Convert.ChangeType吗?

如果该方法始终返回字符串,这让我感到奇怪,但这不是重点,也许下面的修改代码可以实现你想要的功能:

private static T ReadData<T>(XmlReader reader, string value)
{
    reader.MoveToAttribute(value);
    object readData = reader.ReadContentAsObject();
    return (T)Convert.ChangeType(readData, typeof(T));
}

我最初看过Convert.ChangeType,但由于某种奇怪的原因,我决定它对这个操作没有用。你和Bob都提供了相同的答案,我决定采用你们两个答案之间的混合方式,因此我避免使用try语句,但仍然在可能的情况下使用'return (T)readData'。 - Kasper Holdum

16
尝试
if (readData is T)
    return (T)(object)readData;

3

您可以要求类型为引用类型:

 private static T ReadData<T>(XmlReader reader, string value) where T : class
 {
     reader.MoveToAttribute(value);
     object readData = reader.ReadContentAsObject();
     return (T)readData;
 }

然后再做一个使用值类型和TryParse的例子...

 private static T ReadDataV<T>(XmlReader reader, string value) where T : struct
 {
     reader.MoveToAttribute(value);
     object readData = reader.ReadContentAsObject();
     int outInt;
     if(int.TryParse(readData, out outInt))
        return outInt
     //...
 }

3
实际上,这里的问题在于使用了ReadContentAsObject。不幸的是,该方法并没有达到预期的效果;虽然它应该检测值的最合适类型,但实际上无论什么情况下它都返回一个字符串(可以使用Reflector进行验证)。
然而,在你的具体情况下,你已经知道要转换的类型,因此我会说你使用了错误的方法。
尝试使用ReadContentAs方法,这正是你所需要的。
private static T ReadData<T>(XmlReader reader, string value)
{
    reader.MoveToAttribute(value);
    object readData = reader.ReadContentAs(typeof(T), null);
    return (T)readData;
}

看起来非常紧凑和优雅。然而,被接受的答案中的解决方案使用了ChangeType,它与多种不同的文化兼容,因为它接受一个IFormatProvider。由于这对项目是必需的,我将坚持使用该解决方案。 - Kasper Holdum

2
在你期望的T对象中添加一个“class”约束(或更详细地说,像一个基类或接口):
private static T ReadData<T>(XmlReader reader, string value) where T : class
{
    reader.MoveToAttribute(value);
    object readData = reader.ReadContentAsObject();
    return (T)readData;
}

or where T : IMyInterface or where T : new(), etc


2
你可以将一个从字符串到T的转换委托作为参数传入。

1

实际上,这些回应提出了一个有趣的问题,那就是在出现错误的情况下,您希望函数执行什么操作。

也许更合理的做法是构建一个 TryParse 方法,尝试读取 T 的值,但如果无法完成,则返回 false?

    private static bool ReadData<T>(XmlReader reader, string value, out T data)
    {
        bool result = false;
        try
        {
            reader.MoveToAttribute(value);
            object readData = reader.ReadContentAsObject();
            data = readData as T;
            if (data == null)
            {
                // see if we can convert to the requested type
                data = (T)Convert.ChangeType(readData, typeof(T));
            }
            result = (data != null);
        }
        catch (InvalidCastException) { }
        catch (Exception ex)
        {
            // add in any other exception handling here, invalid xml or whatnot
        }
        // make sure data is set to a default value
        data = (result) ? data : default(T);
        return result;
    }

编辑:现在我想想,我真的需要做convert.changetype测试吗?as行不是已经尝试做了吗?我不确定做这个额外的changetype调用是否实际上有任何作用。实际上,它可能只会通过生成异常增加处理开销。如果有人知道有什么区别值得做,请发帖!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接