C#6字符串插值和短格式字符串的bug?

5

C# 6引入了字符串插值和一种更简洁的指定格式字符串的方式。

IntPtr ptr = new IntPtr(0xff);

Console.WriteLine(ptr.ToString());      // 255
Console.WriteLine(ptr.ToString("x"));   // ff

Console.WriteLine($"0x{ptr.ToString("x")}"); // 0xff
Console.WriteLine($"0x{ptr:x}"); //0x255

为什么最后两行输出结果不同?我是否漏掉了什么?

在DotnetFiddle中试一试

顺便提一下,这是dotnet core中IntPtr ToString()的源代码

public unsafe  String ToString(String format) 
    {
        #if WIN32
            return ((int)m_value).ToString(format, CultureInfo.InvariantCulture);
        #else
            return ((long)m_value).ToString(format, CultureInfo.InvariantCulture);
        #endif
    }

$"0x{(int)ptr:x}" // 0xff - vasily.sib
2
你不应该使用 ToString 进行比较 - 更准确的做法是使用 string.Format("{0:x}", ptr); 进行比较,这也会返回 255。 - Damien_The_Unbeliever
2个回答

7

您的示例代码:

Console.WriteLine($"0x{ptr:x}");

等同于它的string.Format兄弟:

Console.WriteLine(string.Format("0x{0:x}", ptr));

应用格式字符串"x"时,字符串插值/字符串格式化最终会到达这行代码
IFormattable formattableArg = arg as IFormattable;

不幸的是,虽然 IntPtr 有一个定制的格式 ToString() 方法,但它没有实现 IFormattable 接口,因此它的基本 .ToString() 方法会被调用,并且格式字符串会被丢弃。

更多信息请参见这个问题

像vasily.sib建议的那样,您可以使用 $"0x{(int)ptr:x}"

尝试我的示例


由于IntPtr没有实现IFormattable接口,为什么ptr.ToString("x")会格式化为十六进制? - KVM
1
@Kvm 正如我所解释的,它有一个自定义的ToString方法,可以接受格式字符串。我甚至在我的答案中提供了一个链接,让你可以阅读它的代码。 - ProgrammingLlama
“格式字符串被丢弃”是框架显然应该检测并在最好的情况下在编译时发出警告的内容(因为插值字符串已经通过编译器进行了相当大的转换),但如果没有,则应在运行时发出一些警告。 - Ben Voigt

0

由于IntPtr不是整数类型,那么为什么ptr.ToString("x")格式化为十六进制? - KVM
如果您想了解更多文档之外的内容,例如您可以查看IntPtr类的ToString方法的实现。 public unsafe string ToString(string format) { return ((long)m_value).ToString(format, CultureInfo.InvariantCulture); } 它在内部使用整数类型(long)。 - fdafadf
进一步推理,"指针不是数值类型",因此那个文档页面和"标准日期/时间格式字符串"一样不适用。 - Ben Voigt

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