2012-07-05 14 views
6

Widziałem wiele sposobów przekształcania Object w String w .NET, zazwyczaj w celu wyświetlania wartości obiektu dla użytkownika, gdy typ obiektu nie jest znany.Najlepsza praktyka przekształcania obiektu na ciąg

Należą do nich:

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) 

Czy istnieje metoda, która jest zalecana przez Microsoft, lub robić te wszystkie skompilować dół do tego samego kodu bajtowego?

EDIT:

Pozwól rozwinąć pytanie trochę obejmuje:

Jakie są różnice między tymi metodami? Nie widzę, co się dzieje pod maską, więc dobrze byłoby wiedzieć, czy jest jakaś zaleta wydajnościowa nad innymi. W niektórych przypadkach połączenia te mogą być wykonywane kilka tysięcy razy (na przykład podczas czytania z dużego stołu), a golenie o kilka sekund może mieć duży wpływ na UX.

Odpowiedz

9

Urządzenie Convert.ToString(x) działa z wdziękiem, nawet jeśli x ma wartość null. W egneralu, kiedy mam do czynienia z materiałami pochodzącymi z bazy danych, myślę, że Convert to najlepsze podejście. Inna sugestia, w przypadku pracy z liczbami zmiennoprzecinkowymi/dziesiętnymi, powinna mieć na uwadze informacje kulturowe, tzn. Nie ufaj im. jako znak dziesiętny, jeśli chcesz założyć, że używasz CultureInfo.InvariantCulture.

+0

Czy wiesz, jeśli jest to zalecana norma Microsoftu? Czy jest jakiś pomysł, co, jeśli w ogóle, narzut, w porównaniu do pozostałych dwóch metod? Nie jestem zbytnio ninja CLR, więc nie mogę ich porównać na poziomie kodu bajtowego. – JDB

+0

Myślę, że pod względem wydajności rzeczy nie zmieniają się sensownie, lepiej być bezpieczniejszym. –

+0

Dzięki - to była najbardziej pomocna odpowiedź. Dodałem odpowiedź z wynikami testu i zasadniczo potwierdziłem twoją sugestię. – JDB

1

Robią różne rzeczy. Kompilują się do różnych kodów MSIL, ale w większości przypadków będą prawdopodobnie miały taki sam wynik.

ToString to metoda zdefiniowana przez Object, która jest nieodłącznym rodzajem bazowym dla wszystkich obiektów. Domyślnie zwraca nazwę typu obiektu, ale może (i często jest) nadpisywany przez każdy typ, dzięki czemu zwraca bardziej znaczący ciąg. Na przykład w twoim przykładzie x jest obiektem Int32, a Int32 przesłania ToString, więc zwraca "3" zamiast domyślnego "System.Int32".

Nie jestem pewien, ale podejrzewam, że po wykonaniu konkatenacji "" & x, to rzuca x do String, w tym przypadku jest to skrót do wpisywania "" & CType(x, String) lub "" & CStr(x). Każdy typ może przeciążyć operatora odlewania, więc zakłada, że ​​typ (w tym przypadku Int32) przeciążył operatora, a zatem może być rzutowany na ciąg. Rzeczywiście ma i może.

Convert.ToString robi różne rzeczy w zależności od tego, do którego połączenia należy zadzwonić. Jeśli przekażesz go jako Int32, po prostu wywołuje metodę obiektu: ToString(). Jeśli jednak przekażesz go na przykład Object, najpierw sprawdza, czy obiekt implementuje IConvertible lub IFormattable. Jeśli tak, używa jednego z nich, w przeciwnym razie używa metody ToString. Próbuje więc, w zależności od typu wysyłanego obiektu, określić, co według niego jest najbardziej prawdopodobnym najlepszym sposobem na wprowadzenie tego typu do ciągu znaków.

Jeśli chodzi o preferowaną metodę, chciałbym powiedzieć, że to, co chcesz używać przez cały czas, chyba że masz jakieś inne obawy (wszystko zależy od tego, co robisz z obiektem).

+0

Odkryłem, że CStr (DBNull.Value) spowoduje wyjątek InvalidCastException, ale "" wartość & DBNull.Value nie będzie. Nie sądzę, że jest to rzutowanie obiektu na ciąg znaków. Ale, "" & Nic również działa, więc nie sądzę, że koniecznie wywołanie ToString(). – JDB

+0

Dzięki za wskazanie ulepszonych możliwości Convert.ToString - nie zdawałem sobie sprawy, że to wszystko. Dodane odbicie spowalnia funkcję nieco w porównaniu do wywoływania ToString(), ale różnica (patrz moja odpowiedź poniżej) jest znikoma. – JDB

1

Postanowiłem przetestować wydajność każdej metody, korzystając z kolekcji 1.000.000 obiektów. Obiekty były jednym z: liczba całkowita, klasa, Nothing lub DBNull.Value. Ta sama kolekcja była używana dla każdego testu, a każdą metodę testowałem 50 razy.

"" & x
To właściwie nie działa dla wszystkich obiektów. Działa dla DBNull.Value i Nothing, ale próba użycia tej metody z dowolnym dowolnym obiektem ol spowoduje wyjątek InvalidCastException. Co ciekawe, CStr (DBNull.Value) generuje wyjątek InvalidCastException, więc nie jestem pewien, dlaczego działa.

Wyniki z niestandardowego obiektu: N/A
Wyniki w/o niestandardowym obiektu Średnia 126,7 ms mediana 126 ms

If(x, "").ToString()
Wyniki z niestandardowymi obiektu Średnia 140,46 MS, mediana 138 ms
Wyniki bez obiektu niestandardowego: śr. 69,32 ms, środkowe 69 ms

Convert.ToString()
Wyniki z niestandardowego obiektu: Średnia 171.54 ms mediana 171 ms
Wyniki w/o niestandardowym obiektu Średnia 112.14 MS mediana 112 ms

Wygląda na to, że numer If(x, "").ToString() jest nieco szybszy w przypadku bardzo dużego zestawu rekordów, ale należałoby to zbalansować z większymi wartościami Convert.ToString() rful opcje konwersji. Dzięki za odpowiedzi.

Oto kod użyłem do testowania:

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 
Powiązane problemy