35

If have this in the setter of a property:

decimal? temp = value as decimal?;

value = "90"

But after the cast, temp is null...

What is the proper way to do this cast?

leppie
  • 115,091
  • 17
  • 196
  • 297
Natrium
  • 30,772
  • 17
  • 59
  • 73

6 Answers6

70

Unboxing only works if the type is identical! You can't unbox an object that does not contain the target value. What you need is something along the lines of

decimal tmpvalue;
decimal? result = decimal.TryParse((string)value, out tmpvalue) ?
                  tmpvalue : (decimal?)null;

This looks whether the value is parsable as a decimal. If yes, then assign it to result; else assign null. The following code does approximately the same and might be easier to understand for people not familiar with the conditional operator ?::

decimal tmpvalue;
decimal? result = null;
if (decimal.TryParse((string)value, out tmpvalue))
    result = tmpvalue;
Margus
  • 19,694
  • 14
  • 55
  • 103
Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • 2
    I wouldn't use the word "identical" here. For instance, you can unbox between enum types and their underlying type, T and T?, and some other odd cases IIRC. The CLR is more permissive than one might expect. – Jon Skeet Feb 09 '09 at 09:42
  • (But yes, you shouldn't expect unboxing to parse a string :) – Jon Skeet Feb 09 '09 at 09:43
  • @Jon: have you got a better formulation? Lacking that, I'm going to copy your comment into my answer because it expresses the caveat nicely. – Konrad Rudolph Feb 09 '09 at 09:58
  • Actually, Parse with one argument is a shortcut for TryParse with style and culture with default arguments.. There is no one to one mapping between string and primitive types, so there's no cast. – thinkbeforecoding Feb 09 '09 at 12:12
  • The case of enums is the only one where you can unbox to another type. Other unboxing simply get the object'ed value and assign it to the variable on the stack. No other conversion occures. – thinkbeforecoding Feb 09 '09 at 12:14
6

you should parse the decimal. But if you want your decimal to be null when the string is not correct, use TryParse :

decimal parsedValue;
decimal? temp = decimal.TryParse(value, out parsedValue)
                ? value
                : (decimal?)null;

This way you will avoid exceptions while parsing ill formated strings.

Almost all primitive types provide a Parse and TryParse methods to convert from string.

Is is also recommended to pass a culture for the provider argument to the method to avoid problems with the decimal separator. If you're reading from another system, CultureInfo.InvariantCulture is probably the way to go (but it's not the default).

bool TryParse(string s, NumberStyles style,
  IFormatProvider provider, out decimal result)
thinkbeforecoding
  • 6,668
  • 1
  • 29
  • 31
4

If you do not want to parse strings, but want to ensure that you receive either null, a decimal or a nullable decimal, then you could do something like this:

public static Nullable<T> Convert<T>(object input) 
    where T : struct
{
    if (input == null)
        return null;
    if (input is Nullable<T> || input is T)
        return (Nullable<T>)input;
    throw new InvalidCastException();
}

You could make it return null on the last line instead if you wished to avoid exceptions, although this would not distinguish between real null values and bad casts.

Note that you have to use the "is" operator, as the "as" operator does not work on value types, and casting without checking may thrown an InvalidCastException.

You could also make it an extension method:

public static class ObjectExtensions
{
    public static Nullable<T> ToNullable<T>(this object input)
        where T : struct
    {
        if (input == null)
            return null;
        if (input is Nullable<T> || input is T)
            return (Nullable<T>)input;
        throw new InvalidCastException();
    }
}

And use it like this:

object value = 123.45m;
decimal? dec = value.ToNullable<decimal>();

This will help avoid code contract warnings about unboxing null references.

Shiva
  • 20,575
  • 14
  • 82
  • 112
Stephen Drew
  • 1,415
  • 19
  • 31
  • Have you tested your extension method?? I tried it and got a NullReferenceException for a valid double value that needed to be converted to nullable decimal – Shiva Mar 16 '17 at 01:24
  • @Shiva The extension method is not intended for doubles, as it states in the first line: "that you receive either null, a decimal or a nullable decimal" – Stephen Drew Feb 01 '18 at 18:36
3

and if you use decimal? temp = (decimal?)value;

The_Black_Smurf
  • 5,178
  • 14
  • 52
  • 78
StevenMcD
  • 17,262
  • 11
  • 42
  • 54
  • that doesn't work. I can't explain why. In debugmode, Visual Studio wants to show the disassembly... – Natrium Feb 09 '09 at 09:06
  • 2
    It won't work if value holds an instance of string. It should work if it holds an instance of decimal. – pauloya Sep 30 '11 at 13:35
2

Surprisingly, but good old System.Convert.ToDecimal(myNullableDoubleBoxedInObject) works perfectly:

decimal? myNullableDecimal = 0.15m;
object myNullableDoubleBoxedInObject = myNullableDouble ;
decimal myDecimal = System.Convert.ToDouble(myNullableDoubleBoxedInObject);
Mr.B
  • 3,484
  • 2
  • 26
  • 40
1

Simple object Extension will also do the trick:

public static class ObjectExtensions
{
    public static decimal ToDecimal(this object obj)
    {
        return System.Convert.ToDecimal(obj);
    }
}
decimal? number = obj?.ToDecimal();