为啥对象没有接受 IFormatProvider 的重载?

Posted

技术标签:

【中文标题】为啥对象没有接受 IFormatProvider 的重载?【英文标题】:Why doesn't object have an overload that accepts IFormatProvider?为什么对象没有接受 IFormatProvider 的重载? 【发布时间】:2011-11-23 04:37:48 【问题描述】:

例如,当将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 上没有这种重载有什么好处?

【问题讨论】:

【参考方案1】:

尝试将您的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));
    

【讨论】:

IConvertible 实际上更有意义(除非您打算格式化数字。) @dlev:我不同意 - OP 特别希望 just 进行格式化。鉴于类型可以轻松实现 IFormattable 但不是 IConvertible,并且 IFormattable 中的单个方法正是 OP 想要调用的方法,我认为 IFormattable 更有意义。 @Jon 根据 OP 的问题,他似乎有实际使用ToString()IConvertible 版本的经验,这就是我提到它的原因。不过,你的观点很好。【参考方案2】:

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();

【讨论】:

【参考方案3】:

尝试以下方法之一:

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")

【讨论】:

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 = 值作为 IFormattable; if (formattable != null) return formattable.ToString(null, provider);返回值 == 空? String.Empty: value.ToString();

以上是关于为啥对象没有接受 IFormatProvider 的重载?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的三角形对象没有被绘制?

为啥 Array.prototype.reduce() 不接受 Map 对象作为初始值?

为啥类型 `Record<string,unknown>` 不接受具有已定义键的对象作为值

StreamWriter 和 IFormatProvider

为啥 OpenFST 似乎没有“运行”或“接受”或“转导”命令?

我应该始终指定要使用哪个 IFormatProvider 吗?