C#中Cast和Convert的区别

4

我有一个sql datareader...从中必须获取一个十进制值。

(decimal)datareader["percent"] 与 Convert.ToDecimal(datareader["percent"])之间有什么区别?

这两种方法的优点和缺点是什么?

6个回答

11

只有当datareader["percent"]返回的对象是Decimal类型时,转换才会成功。如果对象是任何可转换为Decimal类型的类型,包括intlongshort等,那么转换也将成功。更一般地说,任何实现IConvertible接口并从IConvertible.ToDecimal()返回有效值的内容都可以传递给Convert.ToDecimal()

例如:

csharp> object a = (int)1;

csharp> a.GetType();
System.Int32

csharp> var dec = (decimal)a;
System.InvalidCastException: Cannot cast from source type to destination type.
  at Class3.Host (System.Object& $retval) [0x00000] in <filename unknown>:0
  at Mono.CSharp.Evaluator.Evaluate (System.String input, System.Object& result, System.Boolean& result_set) [0x00000] in <filename unknown>:0
  at Mono.CSharpShell.Evaluate (System.String input) [0x00000] in <filename unknown>:0

csharp> var dec = Convert.ToDecimal(a);

csharp> dec;
1

csharp> dec.GetType();
System.Decimal

Howie,你在用什么类型的控制台?看起来你有一个类似 PowerShell 的控制台,可以让你输入 C# 语句。 - Harvey Kwok
3
@Harvey:这是Mono交互式C# shell - cdhowie
这是一个很好的答案,尽管类型转换的解释有点误导。我们可以从byte转换为int。更准确地说,只要类型属于相同的类别,类型转换就可以工作。 - IamIC
@IanC 不是在拆箱时。当你将值类型装箱到对象中时,必须强制转换为与装箱值完全相同的类型才能进行拆箱。(long)(object)(int)1总会产生运行时错误,即使(long)(int)1可以成功。 - cdhowie
当value是double类型时,(decimal)value为什么能够工作? - Nigel Touch
1
@NigelTouch 只有当表达式“value”被输入为“double”时才有效--也就是说,它不是一个取消装箱操作。如果“value”被输入为“object”,那么“double”值将被装箱,您只能将其强制转换为“double”。装箱的值对象只能取消装箱为其精确值类型。 (尝试一下:(decimal)(object)1.0。您将收到一个InvalidCastException。请参见此ideone。) - cdhowie

4

我不清楚具体的十进制转换,但是我知道对于整数,Convert方法会四舍五入,而强制类型转换会截取小数部分,例如(int)7.6得到的结果是7,Convert.ToInt32(7.6)得到的结果是8。虽然这和你的例子无关,但是需要记住这一点。


1

第一个 ((decimal)datareader["percent"]) 是一个显式转换 (强制类型转换)。它的作用是将编译器拆箱或将目标值 (datareader["percent"]) 强制转换为 decimal。这将导致 InvalidCastException,除非 datareader["percent"]decimal 或装箱的 decimal

第二个运行的是 .NET 的代码,检查 datareader["percent"] 是什么对象类型,并尝试以适当的方式将其转换为 decimal。如果值是任何数字类型(例如 int),则会成功。


当value是double类型时,(decimal)value为什么能够工作? - Nigel Touch
@NigelTouch: (decimal)value 是一种有效的转换方式。(decimal)(object)value 是错误的拆箱方式(首先将 double 装箱为 object),会抛出异常。(double)(object)value 是正确的拆箱方式,可以正常工作。 - Jon

1
这里有几个不错的答案,但既然你提到了SqlDataReader,你可以使用GetDecimal()方法而不是使用reader["columnName"]索引器语法。我不知道它是否会为您带来性能上的优势,但它可以提供类型安全,而无需使用Convert或转换。

0
如果问题类型可为空,则对于所有数字类型的转换,它将返回0。 例如,以下所有内容都将返回0
Convert.ToByte(null);
Convert.ToDecimal(null);
Convert.ToDouble(null);
Convert.ToInt64(null);
Convert.ToInt32(null);

但是,如果应用了强制转换,将会抛出InvalidOperationException,并提示“可空对象必须具有值”。


0

编辑

强制类型转换表示对象是指定类型或其派生类。转换则表示虽然它可能不是该类型或派生类,但存在将其转换为目标类型的方法。

例如:

string a = "1234";
object b = a;

// success, a is really a string
string c = (string)b;

// fails because b is not actually an int
int d = (int)b;

// success because there is way to convert the numeric string to an int
int e = Convert.ToInt32(b);

编辑:@cdhowie 指出了一个好点,这应该更加描述性


转换不会在出错时返回类型的默认值:Convert.ToDecimal(new object()); --> System.InvalidCastException: 无法从源类型转换为目标类型。 - cdhowie
应该是我在写第一个版本时想着TryParse,现在已经修改好了。无论如何,现在应该更清晰了。 - Brad Christie

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