将物件投放至T


90

我正在用XmlReader.NET中的类解析XML文件,我认为编写通用的解析函数以通用方式读取不同的属性会很明智。我想出了以下功能:

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

当我意识到时,这并不能完全按照我的计划进行。由于强制类型转换无法将a转换为数值类型,因此它会引发基本类型(例如int或)的错误。有什么办法可以让我的功能以修改后的形式出现?doublestring

Answers:


206

首先检查是否可以投射。

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)以使其可用于各种不同的文化配置。
卡巴斯尔·霍尔顿

2
这是正确的答案。但是我可以说,try / catch在这里完全多余。特别要考虑静音异常。我认为if(readData是T){...}部分是足够的尝试。
pimbrouwers

您可以在转换之前检查readDate是否为null。如果是这样,则返回default(T)。
曼努埃尔·科赫

我得到“对象必须实现IConvertible。”
Casey Crookston,

19

您是否尝试过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'。
卡巴斯尔·霍尔顿

11

尝试

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,因此它与多种不同的区域性兼容。由于这是该项目的必要条件,因此我将继续采用该解决方案。
卡巴斯尔·霍尔顿

2

您大概可以传入一个将作为参数从字符串转换为T的委托作为参数。


1

添加“类”约束(或更详细的约束,例如预期的T对象的基类或接口):

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

where T : IMyInterfacewhere T : new()


1

实际上,响应会提出一个有趣的问题,即在发生错误的情况下您希望函数执行的操作。

也许以试图读取T的TryParse方法的形式构造它会更有意义,但如果无法完成,则返回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调用实际上是否可以完成任何操作。实际上,它可能会通过生成异常来增加处理开销。如果有人知道值得一做的不同之处,请发表!

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.