2013-08-14 9 views
30

Chciałbym wiedzieć, czy istnieje "bezpieczny" sposób konwersji obiektu na unikanie wyjątków.Czy istnieje próba Convert.ToInt32 ... unikanie wyjątków

Szukam czegoś takiego jak public static bool TryToInt32 (wartość obiektu, out int result);

Wiem, że mógłbym zrobić coś takiego:

public static bool TryToInt32(object value, out int result) 
    { 
     try 
     { 
      result = Convert.ToInt32(value); 
      return true; 
     } 
     catch 
     { 
      result = 0; 
      return false; 
     } 
    } 

ale wolałbym uniknąć wyjątków, ponieważ spowalnia proces.

Myślę, że to jest bardziej eleganckie, ale wciąż "tanie".

public static bool TryToInt32(object value, out int result) 
    { 
     if (value == null) 
     { 
      result = 0; 
      return false; 
     } 

     return int.TryParse(value.ToString(), out result); 
    } 

Ktoś ma lepszy pomysł?

UPDATE:

To brzmi trochę jak szukanie włosia przecinania. Jednak konwersja obiektu na ciąg zmusza programistę do utworzenia przejrzystej funkcji ToString().

na przykład:

public class Percentage 
    { 
     public int Value { get; set; } 

     public override string ToString() 
     { 
      return string.Format("{0}%", Value); 
     } 
    } 

     Percentage p = new Percentage(); 
     p.Value = 50; 

     int v; 

     if (int.TryParse(p.ToString(), out v)) 
     { 

     } 

To się nie uda, można zrobić dwie rzeczy tutaj, albo wdrożyć IConvertable jak poniżej.

public static bool ToInt32(object value, out int result) 
    { 
     if (value == null) 
     { 
      result = 0; 
      return false; 
     } 

     if (value is IConvertible) 
     { 
      result = ((IConvertible)value).ToInt32(Thread.CurrentThread.CurrentCulture); 
      return true; 
     } 

     return int.TryParse(value.ToString(), out result); 
    } 

Ale nie można anulować ToInt32 z IConvertible. Więc jeśli nie jest to możliwe, wyjątku nie da się uniknąć.

lub dwa: Czy istnieje sposób sprawdzenia, czy obiekt zawiera niejawny operator?

To jest bardzo słaba:

 if (value.GetType().GetMethods().FirstOrDefault(method => method.Name == "op_Implicit" && method.ReturnType == typeof(int)) != null) 
     { 
      result = (int)value; 
      return true; 
     } 
+1

Dlaczego otaczasz TryParse? –

+0

TryParse ma parametr a string. –

+0

Chcę używać wyjątku tylko wtedy, gdy się go nie spodziewam. Tutaj parametrem może być cokolwiek. To jest POV, z którego pracuję. –

Odpowiedz

36
int variable = 0; 
int.TryParse(stringValue, out variable); 

Jeśli nie może być analizowana zmienna będzie 0. See http://msdn.microsoft.com/en-us/library/f02979c7.aspx

+5

To się nie powiedzie, jeśli przekażemy obiekt do TryParse. –

+11

Bardziej wiarygodne: 'int variable = int.TryParse (stringValue, out variable)? zmienna: 0' –

+1

@KaiHartmann: TryParse oczekuje, że ciąg nie jest obiektem. Ale nawet jeśli użyłeś obiektu i wywołałeś 'int.TryParse (myObject.Tostring(), out result)', to kontynuowałoby działanie dla każdej (nie-zerowej) wartości. Wyjątek Nullreference, który można uzyskać dla wartości null, nie ma nic wspólnego z TryParse, ale raczej z wywołaniem .ToString(). – Flater

6

Nie ma potrzeby, aby ponownie wymyślać koła tutaj. użyj int.TryParse, aby osiągnąć swój cel. Zwraca wartość bool, aby pokazać, że wartość jest analizowana lub nie. i jeśli parsowany wynik jest zapisywany w zmiennej wyjściowej.

int result; 
object a = 5; 
if(int.TryParse(a.ToString(),out result)) 
{ 
    Console.WriteLine("value is parsed"); //will print 5 
}    

object b = a5; 
if(int.TryParse(b.ToString(),out result)) 
{ 
   Console.WriteLine("value is parsed");   
} 
else 
{ 
    Console.WriteLine("input is not a valid integer");  //will print this 
} 
+0

To się nie powiedzie, jeśli przekażemy obiekt do TryParse. –

+3

@KaiHartmann tak, potrzebuje łańcucha. A jeśli masz obiekt, musisz najpierw wywołać metodę ToString. zaktualizowałem moją odpowiedź. – Ehsan

1

Chciałbym użyć mieszaniny tego, co już robisz;

  • Sprawdź, czy obiekt jest pusty - zwraca wartość false, a wartość 0;
  • Próba bezpośredniej konwersji - w przypadku powodzenia zwracana jest wartość true i przekształcona wartość
  • Próba przeanalizowania wartości.ToString() - jeżeli udana, powrót prawdziwej i analizowana wartość
  • każdym innym przypadku - return false i wartość 0, a obiekt nie jest zamienny/parsible

Otrzymany kod:

public static bool TryToInt32(object value, out int result) 
{ 
    result = 0; 
    if (value == null) 
    { 
     return false; 
    } 

    //Try to convert directly 
    try 
    { 
     result = Convert.ToInt32(value); 
     return true; 
    } 
    catch 
    { 
     //Could not convert, moving on 
    } 

    //Try to parse string-representation 
    if (Int32.TryParse(value.ToString(), out result)) 
    { 
     return true; 
    } 

    //If parsing also failed, object cannot be converted or paresed 
    return false; 
} 
+0

Dlaczego powinienem przekazać to jako odniesienie? –

+0

@JeroenvanLangen, ponieważ wynik powinien zachować oryginalną wartość, jeśli nie jest konwertowany - zaktualizowałem odpowiedź. –

+0

@JeroenvanLangen, Wygląda na to, że się pomyliłem. Zaktualizuję odpowiedź jeszcze raz. Ale mimo wszystko ten potrójny czek wydaje się być tym, czego chcesz. –

9

Spurring from the comments. Odpowiedź jest nr. Nie możesz zrobić tego, co robi Convert.ToInt32(object), nie rzucając wyjątków. Możesz zrobić coś podobnego (i już to zrobiłeś). Jedyną rzeczą, którą mógłbym zoptymalizować, jest przypadek value już będący int.

if (value is int) 
    return (int)value; 

Nie można tego zrobić, bo jak Convert.ToInt32(object)Convert.ToInt32(object) nie po prostu sprawdzić, czy value jest short, int, long, ushort, ... a następnie rzucać je. Sprawdza, czy value jest IConvertible. Jeśli tak, używa IConvertible.ToInt32. Niestety interfejs IConvertible jest dość słaba: nie ma non-rzucania metodami (IConvertible.Try*)

Choć głupie (ale chyba nie za dużo), ktoś mógłby zrobić na przykład UnixDateTime struct: (unixtime to liczba sekund od północy 1970-01-01), gdzie IConvertible.ToInt32 zwraca tę liczbę sekund, podczas gdy ToString() zwraca sformatowaną datę. Wszystkie int.TryParse(value.ToString(), out parsed) dusi się, a Convert.ToInt32 będzie działać bezbłędnie.

+0

Jeśli użyjemy IConvertible jako parametru w metodzie zamiast obiektu, czy to nie rozwiąże problemu? Na przykład zobacz moją odpowiedź. –

+0

@KaiHartmann Nie, jeśli masz już 'obiekt'. 'object obj = 5,5'. 'MyConvert.ToInt32()' wybiera przeciążenie 'object' zamiast przeciążenia' IConvertible'. – xanatos

+0

Usługa IConvertible nadal może zgłosić wyjątek. –

3

Ta wersja użyciu konwertera typu będzie konwertować tylko ciąg w ostateczności, ale też nie rzucać wyjątek:

public static bool TryToInt32(object value, out int result) 
{ 
    if (value == null) 
    { 
     result = 0; 
     return false; 
    } 
    var typeConverter = System.ComponentModel.TypeDescriptor.GetConverter(value); 
    if (typeConverter != null && typeConverter.CanConvertTo(typeof(int))) 
    { 
     var convertTo = typeConverter.ConvertTo(value, typeof(int)); 
     if (convertTo != null) 
     { 
      result = (int)convertTo; 
      return true; 
     } 
    } 
    return int.TryParse(value.ToString(), out result); 
} 
+0

+1 za to, że jest interesującą i inną odpowiedzią. Ale to naprawdę jest * bardzo * okrężny sposób używania tych samych starych metod IConvertible. Częścią problemu jest tutaj również metoda "CanConvertTo". Po prostu nie robi to, czego chcemy - chcemy, aby odpowiedź była zależna od rzeczywistej wartości: chcemy, by długa była odlewana do int * iff wartości przechowywanej w tym długim pasowaniu w int *. Ta metoda wyrzuciłaby wyjątek przepełnienia, jeśli próbujesz rzucić 'long.MaxValue' na int, ponieważ' CanConvertTo' nie robi tego, co myślisz, że robi, w rzeczywistości zwróci true dla ** wszystkich ** typów pierwotnych! – AnorZaken

0

pisałem ten bałagan, patrząc na to mnie smuci.

using System; 
using System.Globalization; 

internal static class ObjectExt 
{ 
    internal static bool TryConvertToDouble(object value, out double result) 
    { 
     if (value == null || value is bool) 
     { 
      result = 0; 
      return false; 
     } 

     if (value is double) 
     { 
      result = (double)value; 
      return true; 
     } 

     var text = value as string; 
     if (text != null) 
     { 
      return double.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out result); 
     } 

     var convertible = value as IConvertible; 
     if (convertible == null) 
     { 
      result = 0; 
      return false; 
     } 

     try 
     { 
      result = convertible.ToDouble(CultureInfo.InvariantCulture); 
      return true; 
     } 
     catch (Exception) 
     { 
      result = 0; 
      return false; 
     } 
    } 
} 

Edit Wskazówka teraz odpowiedziałem na pytanie, kiedy dwukrotnie był int, utrzymując to jakiś sposób. Może przydatne dla kogoś.

-1

Dlaczego po prostu tego nie naprawisz w swoim kodzie wywołującym? np

using System; 
public class Model 
{ 
    public byte? ByteId; 
    public int IdToInt() 
    { 
     return Convert.ToInt32(ByteId ?? 0); 
    } 
} 

ten sposób nie zmianę/odbiegają od znanego zachowania bibliotek DotNet których tylko myli kolegów, i to wyraźnie widoczne, co domyślnie dostajesz po stronie wywołującego (0, -1).