为什么对象没有重载接受IFormatProvider的方法?

29

将例如decimal转换为string时,您使用CultureInfo.InvariantCulture并将其作为IFormatProvider传递。但为什么这个重载不在object中呢?

一个好的实现方式是:

public virtual string ToString()
{
   // yadayada, usual ToString
}

public virtual string ToString(IFormatProvider provider)
{
   return ToString();
}

这不会对 object 类造成任何伤害或益处,但是从它派生的对象可以覆盖重载,当您不确定类型时调用它将变得更加容易。

我遇到的问题是,当我正在编写一个方法来获取类的所有属性并将其写入 XML 时。由于我不想检查对象的类型,所以我只调用了 ToString 。 但是如果这是十进制数,则输出将基于线程的 CurrentCulture ,这是不理想的。 我唯一看到的解决办法是将 CurrentCulture 更改为 InvariantCulture ,然后再改回以前的文化。 但这只会让代码变丑,因为我必须编写 try finally 块等。

我的当前代码如下:

        foreach (var property in typeof(Order).GetProperties(BindingFlags.Public | BindingFlags.Instance).
            Where(c => ValidTypes.Contains(c.PropertyType)))
        {
            var value = property.GetValue(order, null);
            if (value != null)
            {
                writer.WriteElementString(property.Name, 
                value.ToString());
            }
        }

但我希望它是这样的:

        foreach (var property in typeof(Order).GetProperties(BindingFlags.Public | BindingFlags.Instance).
            Where(c => ValidTypes.Contains(c.PropertyType)))
        {
            var value = property.GetValue(order, null);
            if (value != null)
            {
                writer.WriteElementString(property.Name, 
                value.ToString(CultureInfo.InvariantCulture));
            }
        }

不在object上重载的任何好处吗?

3个回答

29
尝试将您的"value"转换为"IFormattable":
foreach (var property in typeof(Order).GetProperties(BindingFlags.Public | BindingFlags.Instance).
       Where(c => ValidTypes.Contains(c.PropertyType)))
{
    var value = property.GetValue(order, null);
    if (value != null)
    {
        var formattable = value as IFormattable;
        writer.WriteElementString(property.Name, 
        formattable == null ? value.ToString() : formattable.ToString(null, CultureInfo.InvariantCulture));
    }
}

3
“IConvertible”实际上更有意义(除非您计划格式化数字)。 - dlev
5
我不同意 - OP只是想格式化。考虑到类型可以很容易地实现IFormattable而不实现IConvertible,并且IFormattable中的单个方法正是OP想调用的方法,我认为选择IFormattable更合理。 - Jon Skeet
基于原帖的问题,看起来他已经有使用IConvertible版本的ToString()的经验,这就是为什么我提到它的原因。不过,你也说得很对。 - dlev

16

Peter的解决方案的便捷扩展方法(修改后也测试IConvertible)。

public static string ToInvariantString(this object obj)
{
    return obj is IConvertible ? ((IConvertible)obj).ToString(CultureInfo.InvariantCulture)
        : obj is IFormattable ? ((IFormattable)obj).ToString(null, CultureInfo.InvariantCulture)
        : obj.ToString();
}

8

请尝试以下方法之一:

string valueString = XmlConvert.ToString(value);
string valueString = Convert.ToString(value, CultureInfo.InvariantCulture);

XmlConvert.ToString()是专门用于处理XML的,因此它会更加符合XML规范,例如使用“true”而不是“True”。但是,它比Convert.ToString()更加脆弱。例如,由于UTC时间,它会抛出异常:

XmlConvert.ToString(DateTime.UtcNow)

但这个是有效的:
XmlConvert.ToString(DateTime.UtcNow, "o")

3
Convert.ToString实际上将类型转换为IConvertible和IFormattable(参见前面的帖子):public static string ToString(Object value, IFormatProvider provider) { IConvertible ic = value as IConvertible; if (ic != null) return ic.ToString(provider); IFormattable formattable = value as IFormattable; if (formattable != null) return formattable.ToString(null, provider); return value == null? String.Empty: value.ToString(); } - Dariusz Wasacz

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