Is there a try Convert.ToInt32... avoiding exceptions
Asked Answered
I

8

61

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;
}
Impetuosity answered 14/8, 2013 at 8:55 Comment(8)
Why are you wrapping the TryParse?Degrade
TryParse has a string a parameter.Impetuosity
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.Impetuosity
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.Shikari
To your code I would add a check if value is already an int. if (value is int) return (int)value;Shikari
@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; }Impetuosity
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.Bough
Instead of Object.ToString use String.Format with a numeric format specifier, i.e. D, this will not require objects to override default ToString.Dehlia
B
60
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

Britteny answered 14/8, 2013 at 9:1 Comment(5)
This would fail, if you pass an object to TryParse.Chobot
More reliable: int variable=int.TryParse(stringValue, out variable) ? variable : 0Concussion
@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().Typesetting
If stringValue equals "0" it will be successfully parsed and variable will still be 0. You should check the return value of TryParse.Josphinejoss
The answer from @Rango is the best answer in my use case for now.Wortman
S
16

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.

Shikari answered 14/8, 2013 at 9:30 Comment(2)
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.Chobot
@KaiHartmann Not if you already have an object. object obj = 5.5. MyConvert.ToInt32() would select the object overload instead of the IConvertible overload.Shikari
S
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);
}
Schaper answered 31/5, 2016 at 11:12 Comment(1)
+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!Bough
L
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   
}
Laplante answered 14/8, 2013 at 9:2 Comment(2)
This would fail, if you pass an object to TryParse.Chobot
@KaiHartmann yes it needs a string. And if you have object you will have to call the ToString method on that first. updated my answer.Laplante
P
3

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

int? value = int.TryParse(stringValue, out int outValue) 
    ? outValue
    : default(int?);
Preachment answered 2/11, 2018 at 15:35 Comment(2)
Try methods must return bool.Dehlia
@Dehlia this TryParse method does. I'm using a ternary operator. The boolean that is returned from the TryParse is used to determine whether to return outValue or default(int?)Preachment
G
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;
}
Gender answered 14/8, 2013 at 9:24 Comment(3)
Why should i pass it as reference?Impetuosity
@JeroenvanLangen, Because the result should keep its original value, if it is not converted - I've updated the answer.Gender
@JeroenvanLangen, It seems I was mistaken. I will update the answer again. But still, this triple-check seems to be what you want.Gender
P
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.

Percyperdido answered 16/9, 2016 at 15:23 Comment(0)
G
0

This is how I like to do it:

object v = someValue;
if (int.TryParse($"{v}", out var extractedValue))
{
   // do something with extractedValue
}
Garrard answered 7/1, 2023 at 0:41 Comment(1)
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.Citole

© 2022 - 2024 — McMap. All rights reserved.