2012-07-05 60 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