2012-07-05 61 views
6

我曾见过很多方法将.NET中的Object转换为String,通常用于在对象类型未知时向用户显示对象的值。将对象转换为字符串的最佳做法

这些措施包括:

Dim x as Object = 3 
Dim y as Object = Nothing 
Dim z as Object = DBNull.Value 
Dim l_displayString As String 

l_displayString = "" & x & "" & y & "" & z 
l_displayString = If(x, "").ToString() & If(y, "").ToString() & If(z, "").ToString() 
l_displayString = Convert.ToString(x) & Convert.ToString(y) & Convert.ToString(z) 

是否有这是由微软推荐的方法,或者说这些都向下编译到相同的字节码?

编辑:

让我扩大问题有点包括:

什么是这些方法之间的区别?我无法看到底下发生了什么,所以很高兴知道其中一个是否有任何性能优势。在某些情况下,这些调用可能会进行数千次(例如从大型表中读取),并且几秒钟之后削减可能会对用户体验产生巨大影响。

回答

9

即使x为空,Convert.ToString(x)也能正常工作。在egneral中,当处理来自数据库的东西时,我认为Convert是最好的方法。另一个建议是,当使用浮点数/小数时,注意CultureInfo,即不要相信。作为十进制符号,如果你想假设使用CultureInfo.InvariantCulture

+0

你知不知道这是否是微软的推荐标准?有什么想法,如果有的话,开销与其他两种方法相比?我不是一个CLR忍者,所以我不能在字节代码级别比较它们。 – JDB 2012-07-05 15:53:30

+0

我认为在表现方面,事情并没有明显改变,最好是更安全。 – 2012-07-05 15:57:33

+0

谢谢 - 这是最有用的答案。我用我的测试结果添加了一个答案,它基本上证实了你的建议。 – JDB 2012-07-05 17:23:45

1

他们做不同的事情。它们编译为不同的MSIL代码,但在大多数情况下,它们可能具有相同的结果。

ToString是由Object定义的方法,它是所有对象的固有基本类型。默认情况下,它返回对象的类型名称,但它可以(并且经常)被每个类型覆盖,以便返回更有意义的字符串。例如,在您的示例中,xInt32对象,而Int32将覆盖ToString,因此它将返回"3"而不是默认的“System.Int32”。

我还不能肯定,但是当你做串联"" & x我怀疑,这是铸造xString,在这种情况下,它是一个快捷方式到打字"" & CType(x, String)"" & CStr(x)。每种类型都可以重载铸造操作符,因此它假定类型(在本例中为Int32)已经重载了操作符,因此可以将其转换为字符串。事实上,它有,可以。

Convert.ToString根据您调用的超载而做不同的事情。如果你通过它Int32,它只是调用对象的ToString()方法。但是,例如,如果您通过Object,它首先会检查对象是否实施IConvertibleIFormattable。如果是,则使用其中的一种,否则使用ToString方法。因此,它会根据您发送的对象类型来尝试确定它认为是将该类型转换为字符串的最可能的最佳方式。

至于什么是首选的方法,我会说x.ToString()是你想要使用最多的时间,除非你有其他的关注(这一切都取决于你对对象做什么)。

+0

我发现CStr(DBNull.Value)会导致InvalidCastException,但是“”&DBNull.Value不会。所以我不认为它是将对象转换为输入字符串。但是,“&Nothing也行,所以我不认为它必须调用ToString()。 – JDB 2012-07-05 16:16:51

+0

感谢您指出Convert.ToString的增强功能 - 没有意识到它所做的一切。与调用ToString()相比,添加的反射会使函数慢下来,但差异(请参阅下面的答案)可以忽略不计。 – JDB 2012-07-05 17:25:03

1

我决定使用一组1,000,000个对象来测试每种方法的性能。对象是以下之一:整数,类,Nothing或DBNull.Value。每个测试使用相同的集合,并且我测试了每种方法50次。

"" & x
这实际上不适用于所有对象。它适用于DBNull.Value和Nothing,但试图将这个方法与任何ol对象一起使用会导致InvalidCastException。有趣的是,CStr(DBNull.Value)抛出一个InvalidCastException,所以我不确定它为什么起作用。

结果与自定义对象:N/A
结果的w/o的自定义对象:AVG 126.7毫秒,中值126毫秒

If(x, "").ToString()
结果与自定义对象:AVG 140.46毫秒,位数138毫秒
没有自定义对象的结果:avg 69.32 ms,中位数69毫秒

Convert.ToString()
结果与自定义对象:AVG 171.54毫秒,中值171毫秒
结果的w/o的自定义对象:AVG 112.14毫秒,中值112毫秒

所以看起来If(x, "").ToString()是一个非常大的一套记录快一点,但这将需要与Convert.ToString()的更多powe平衡有效的转换选项。感谢您的答案。

下面是我用于测试的代码:

Option Strict Off 

Module Module1 

    Sub Main() 
     Dim l_objectArray = Enumerable.Range(0, 1000000).Select(Function(x) GetObject(x)).ToArray() 

     Dim l_stopWatch As New Stopwatch() 
     Dim l_testResults As New List(Of Long) 
     Dim l_testIterations As Integer = 50 
     Dim l_displayValue As String 

     Do 

      ' -------------------- 

      'Console.WriteLine() 
      'Console.WriteLine("Conversion using string concatenation") 
      'l_testResults.Clear() 

      'For iteration = 0 To l_testIterations - 1 
      ' l_stopWatch.Start() 
      ' For Each o In l_objectArray 
      '  l_displayValue = "" & o 
      ' Next 
      ' l_stopWatch.Stop() 
      ' l_testResults.Add(l_stopWatch.ElapsedMilliseconds) 
      ' l_stopWatch.Reset() 
      'Next 

      'Console.WriteLine() 
      'Console.WriteLine("Average: " & l_testResults.Average()) 
      'Console.WriteLine("Median: " & GetMedian(l_testResults.ToArray())) 

      ' -------------------- 

      Console.WriteLine() 
      Console.WriteLine("Conversion using Object.ToString()") 
      l_testResults.Clear() 

      For iteration = 0 To l_testIterations - 1 
       l_stopWatch.Start() 
       For Each o In l_objectArray 
        l_displayValue = If(o, "").ToString() 
       Next 
       l_stopWatch.Stop() 
       l_testResults.Add(l_stopWatch.ElapsedMilliseconds) 
       l_stopWatch.Reset() 
      Next 

      Console.WriteLine() 
      Console.WriteLine("Average: " & l_testResults.Average()) 
      Console.WriteLine("Median: " & GetMedian(l_testResults.ToArray())) 

      ' -------------------- 

      Console.WriteLine() 
      Console.WriteLine("Conversion using Convert.ToString(x)") 
      l_testResults.Clear() 

      For iteration = 0 To l_testIterations - 1 
       l_stopWatch.Start() 
       For Each o In l_objectArray 
        l_displayValue = Convert.ToString(o) 
       Next 
       l_stopWatch.Stop() 
       l_testResults.Add(l_stopWatch.ElapsedMilliseconds) 
       l_stopWatch.Reset() 
      Next 

      Console.WriteLine() 
      Console.WriteLine("Average: " & l_testResults.Average()) 
      Console.WriteLine("Median: " & GetMedian(l_testResults.ToArray())) 

      ' -------------------- 

      Console.WriteLine() 
      Console.Write("Exit? (y/n): ") 
      Dim l_key = Console.ReadKey(False) 
      If l_key.Key = ConsoleKey.Y Then 
       Exit Sub 
      End If 

     Loop 

    End Sub 

    Private Function GetMedian(ByVal values As Long()) As Long 
     Array.Sort(values) 
     If values.Length Mod 2 = 0 Then 
      Return (values(values.Length/2) + values(values.Length/2 - 1))/2 
     Else 
      Return values(CInt(Math.Floor(values.Length/2))) 
     End If 
    End Function 

    Private Function GetObject(ByVal someNumber As Integer) As Object 
     Select Case someNumber Mod 4 
      Case 0 
       Return someNumber 
      Case 1 
       Return New SomeClass(someNumber) 
       'Return Nothing 
      Case 2 
       Return DBNull.Value 
      Case Else 
       Return Nothing 
     End Select 
    End Function 

    Private Class SomeClass 

     Private _seed As Integer 

     Public Sub New(ByVal seed As Integer) 
      _seed = seed 
     End Sub 

     Public Overrides Function ToString() As String 
      Return _seed.ToString() 
     End Function 

    End Class 

End Module