60

I'd like to know if there is a "safe" way to convert an object to an int, avoiding exceptions.

I'm looking for something like public static bool TryToInt32(object value, out int result);

I know I could make something like this:

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

But I'd rather avoid exceptions, because they are slowing down the process.

I think this is more elegant, but it's still "cheap":

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

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

Does anyone have better ideas?

UPDATE:

This sounds a little like splitting hairs, but converting an object to string forces the implementer to create a clear ToString() function. For example:

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))
{

}

This goes wrong, I can do two things here, or implement the IConvertable like this:

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);
}

But the ToInt32 method of the IConvertible cannot be canceled. So if it's not possible to convert the value, an exception cannot be avoided.

Or two: Is there a way to check if the object contains a implicit operator?

This is very poor:

if (value.GetType().GetMethods().FirstOrDefault(method => method.Name == "op_Implicit" && method.ReturnType == typeof(int)) != null)
{
    result = (int)value;
    return true;
}
CarenRose
  • 1,266
  • 1
  • 12
  • 24
Jeroen van Langen
  • 21,446
  • 3
  • 42
  • 57
  • 1
    Why are you wrapping the TryParse? – Adriaan Stander Aug 14 '13 at 08:57
  • TryParse has a string a parameter. – Jeroen van Langen Aug 14 '13 at 08:58
  • I only want to use exception if i'm not expecting one. Here the parameter could be anything. So thats the POV i'm working from. – Jeroen van Langen Aug 14 '13 at 09:00
  • 1
    Be aware that `Convert.ToInt32` does something different. It tries to cast the object to `IConvertible` and then call the method `ToInt32`. There is a subtle difference: any class could implement `IConvertible`, but could have a `ToString()` that doesn't return a stringified number. – xanatos Aug 14 '13 at 09:02
  • To your code I would add a check if value is already an int. `if (value is int) return (int)value;` – xanatos Aug 14 '13 at 09:15
  • @xanatos, i think the IConvertable interface is nice, but the interface won't allow to cancel/try_convert a value. The implementer still needs to throw an exception if a value cannot be converted. _if (value is IConvertible) { result = ((IConvertible)value).ToInt32(Thread.CurrentThread.CurrentCulture); return true; }_ – Jeroen van Langen Aug 14 '13 at 09:22
  • Wow, none of the existing answers adequately answer the question.. I'm too sleepy to write an answer but basically if you're working with primitive integral types use `TypeCode` to determine if input is unsigned `((int)typeCode - 5 & 9) == 1` (assuming you don't care about char and bool) or if it is signed `((int)typeCode - 5 & 9) == 0` and use `ToUInt64` or `ToInt64` accordingly, then check bounds and profit. All `IConvertible` types implement `GetTypeCode()`. Now of course if you also want to be able to do it for custom types, add your own interface to them, and check for it in the convert. – AnorZaken Jun 08 '17 at 04:18
  • Instead of Object.ToString use String.Format with a numeric format specifier, i.e. D, this will not require objects to override default ToString. – user3285954 Mar 14 '20 at 16:55

8 Answers8

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

If it can't be parsed, the variable will be 0. See http://msdn.microsoft.com/en-us/library/f02979c7.aspx

ranieuwe
  • 2,268
  • 1
  • 24
  • 30
  • 9
    This would fail, if you pass an object to TryParse. – Kai Hartmann Aug 14 '13 at 11:20
  • 25
    More reliable: `int variable=int.TryParse(stringValue, out variable) ? variable : 0` – Tim Schmelter Aug 20 '14 at 21:12
  • 1
    @KaiHartmann: TryParse expects a string not an object. But even if you used an object and called `int.TryParse(myObject.Tostring() , out result)`, it would keep working for every (non-null) value. The Nullreference exception you'd get for null values has nothing to do with the TryParse, but rather with calling .ToString(). – Flater Jan 10 '17 at 13:30
  • 1
    If `stringValue` equals "0" it will be successfully parsed and `variable` will still be `0`. You should check the return value of `TryParse`. – runeks Sep 03 '18 at 15:22
  • The answer from @Rango is the best answer in my use case for now. – wlwl2 Jan 17 '19 at 23:26
15

Spurring from the comments. The response is no. You can't do what Convert.ToInt32(object) does without having throwed exceptions. You can do something similar (and you already did it). The only thing I would optimize is the case of value already an int.

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

You can't do as Convert.ToInt32(object) because Convert.ToInt32(object) doesn't simply test if value is short, int, long, ushort, ... and then cast them. It checks if the value is IConvertible. If yes it uses the IConvertible.ToInt32. Sadly the interface IConvertible is quite poor: it doesn't have non-throwing methods (IConvertible.Try*)

While stupid (but perhaps not too much), someone could make for example a UnixDateTime struct: (UnixTime is the number of seconds from midnight 1970-01-01), where the IConvertible.ToInt32 returns this number of seconds, while the ToString() returns a formatted date. All the int.TryParse(value.ToString(), out parsed) would choke, while the Convert.ToInt32 would work flawlessly.

xanatos
  • 109,618
  • 12
  • 197
  • 280
  • If we use an IConvertible as parameter in the method instead of object, wouldn't that solve the problem? For an example, see my answer. – Kai Hartmann Aug 14 '13 at 09:36
  • @KaiHartmann Not if you already have an `object`. `object obj = 5.5`. `MyConvert.ToInt32()` would select the `object` overload instead of the `IConvertible` overload. – xanatos Aug 14 '13 at 09:40
6

This version using a type converter would only convert to string as a last resort but also not throw an exception:

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);
}
Jeremy Thomas
  • 189
  • 2
  • 4
  • 3
    +1 for being an interesting and different answer. But this really is a *very* roundabout way of using the same old IConvertible methods. Part of the problem here is also the `CanConvertTo` method. It simply doesn't do what we want - we want the answer to depend on the actual value: we want a long to be castable to an int *iff the value stored in that long fits in an int*. This method would throw an overflow exception if trying to cast `long.MaxValue` to an int, because `CanConvertTo` does not do what you think it does, in fact it will return true for **all** primitive types! – AnorZaken Jun 08 '17 at 03:57
5

No need to re-invent the wheel here. use int.TryParse to achieve your goal. It returns a bool to show that value is parsed or not. and if parsed the result is saved in the output variable.

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   
}
Ehsan
  • 31,833
  • 6
  • 56
  • 65
2

Return a nullable int. that way you know whether you parsed 0.

int? value = int.TryParse(stringValue, out int outValue) 
    ? outValue
    : default(int?);
chris31389
  • 8,414
  • 7
  • 55
  • 66
1

I would use a mixture of what you are already doing;

  • Check if the object is null - return false and the value 0;
  • Attempt to convert directly - if successful, return true and the converted value
  • Attempt to parse value.ToString() - if successfull, return true and the parsed value
  • Any other case - Return false and the value 0, as object is not convertible/parsible

The resulting code:

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;
}
Lars Kristensen
  • 1,410
  • 19
  • 29
1

I wrote this mess, looking at it makes me sad.

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 Notice now I answered for double when the question was int, keeping it any way. Maybe useful for someone.

Johan Larsson
  • 17,112
  • 9
  • 74
  • 88
0

This is how I like to do it:

object v = someValue;
if (int.TryParse($"{v}", out var extractedValue))
{
   // do something with extractedValue
}
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jan 07 '23 at 13:34