将对象转换为十进制数?(可空十进制数)

35
如果在属性的 setter 中有以下代码:
decimal? temp = value as decimal?;

value = "90"

但是在转换后,temp变成了null...

如何才能正确地进行这种类型转换?

6个回答

70

只有类型相同才能进行拆箱操作!你不能拆箱一个不包含目标值的 object。你需要类似于下面的东西:

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

这段代码判断一个值是否可解析为 decimal 类型,如果是,则将其赋给 result;否则将赋值为 null。以下代码大致相同,但可能更容易理解,适合不太熟悉条件运算符 ?: 的人:

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

2
我不会在这里使用“相同”的词。例如,您可以在枚举类型及其基础类型之间、T 和 T? 之间进行拆箱操作,以及其他一些奇怪的情况(如果我没记错的话)。CLR 比人们预期的更宽容。 - Jon Skeet
但是,您不应该指望 unboxing 能解析字符串 :) - Jon Skeet
@Jon:你有更好的表述吗?如果没有,我会把你的评论复制到我的答案中,因为它很好地表达了警告。 - Konrad Rudolph
实际上,带有一个参数的解析是尝试解析默认参数样式和文化的快捷方式。字符串和基本类型之间没有一对一的映射,因此没有强制转换。 - thinkbeforecoding
枚举类型是唯一可以拆箱为另一种类型的情况。其他拆箱只是获取对象值并将其分配给堆栈上的变量,不会发生任何其他转换。 - thinkbeforecoding

6

你应该解析十进制数。但如果你希望在字符串不正确时使你的十进制数为空,使用TryParse:

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

通过这种方式,您将避免解析格式不正确的字符串时出现异常。

几乎所有基本类型都提供了Parse和TryParse方法来从字符串转换。

建议为方法传递一个区域设置以避免十进制分隔符的问题。如果您正在从另一个系统读取,则CultureInfo.InvariantCulture可能是最好的选择(但它不是默认值)。

bool TryParse(string s, NumberStyles style,
  IFormatProvider provider, out decimal result)

我忘记了对空值进行(十进制?)转换...抱歉,但文化部分仍然很重要。 - thinkbeforecoding
3
这个答案是错误的,因为将'value'赋值给'temp'不起作用。正确的方式应该是? parsedValue : (decimal?)null; - offner

4

如果你不想解析字符串,但希望确保只接收到 nulldecimal 或可空的 decimal 之一,那么可以像这样操作:

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

如果您希望避免异常,您可以在最后一行返回null,但这样做将无法区分真正的null值和错误的强制转换。
请注意,您必须使用“is”运算符,因为“as”运算符不适用于值类型,而没有检查的强制转换可能会引发InvalidCastException异常。
您还可以将其作为扩展方法实现:
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();
    }
}

并像这样使用:

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

这将有助于避免代码合同警告关于拆箱空引用。

你测试过你的扩展方法吗?我尝试了一下,对于需要转换为可空十进制的有效双精度值,我得到了一个 NullReferenceException。 - Shiva
@Shiva 扩展方法不适用于 double,正如第一行所述:“您接收 null、decimal 或可空 decimal”。 - Stephen Drew

3

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


那不起作用。我无法解释为什么。在调试模式下,Visual Studio想要显示反汇编... - Natrium
2
如果value保存的是字符串实例,它将无法工作。如果它保存的是十进制实例,它应该可以工作。 - pauloya

2

令人惊讶的是,但是老式的System.Convert.ToDecimal(myNullableDoubleBoxedInObject)非常完美地工作:

decimal? myNullableDecimal = 0.15m;
object myNullableDoubleBoxedInObject = myNullableDouble ;
decimal myDecimal = System.Convert.ToDouble(myNullableDoubleBoxedInObject);

1

简单对象扩展也可以解决问题:

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

decimal? number = obj?.ToDecimal();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接