强制转换与使用 Convert.To()方法之间的区别

我有一个函数,它对 string值强制执行 double

string variable = "5.00";


double varDouble = (double)variable;

签入了代码更改,项目生成时出现错误: System.InvalidCastException: Specified cast is not valid.

然而,在做了以下事情之后..。

string variable = "5.00";


double varDouble = Convert.ToDouble(variable);

... 项目建设没有任何错误。

铸造和使用 Convert.To()方法有什么区别?为什么抛出一个 Exception而使用 Convert.To()不会?

55488 次浏览

Convert.Double方法实际上只是在内部调用 Double.Parse(string)方法。

String类型和 Double类型都没有定义两种类型之间的显式/隐式转换,因此强制转换总是会失败。

Double.Parse方法将查看 string中的每个字符,并根据 string中字符的值构建一个数值。如果任何字符无效,则 Parse方法失败(导致 Convert.Double方法也失败)。

在您的示例中,您试图将字符串强制转换为 double (非整数类型)。

需要进行显式转换才能正常工作。

我必须指出,你可以使用 Convert.ToDouble而不是 Convert.ToInt64,因为当你转换为 int 时,你会丢失双精度值的小数部分。

如果变量的值为“5.25”,varDouble 应该是5.00(由于转换为 Int64而损失了0.25)

回答你关于铸造和转换的问题。

您的强制转换(显式强制转换)不符合显式强制转换的要求。您试图用强制转换运算符强制转换的值无效(即非积分)。

访问此 MSDN 网页了解强制转换/转换规则

string variable = "5.00";
double varDouble = (double)variable;

语言不允许以上的转换。下面是数值类型的显式强制转换列表: http://msdn.microsoft.com/en-us/library/yht2cx7b.aspx正如您所看到的,即使不是每个数值类型都可以转换为另一个数值类型

更多关于选角 给你的信息

这和 Convert.ToDouble ()有什么区别?

强制转换类型时,不更改数据结构。那么,在数值转换的情况下,你可能会失去一些位或得到一些额外的0位。你只是改变了这个数字占用的内存量。这对于编译器来说足够安全了。

但是当您试图将字符串强制转换为一个数字时,您不能这样做,因为仅仅改变变量占用的内存量是不够的。例如,5点作为字符串是一个“数字”序列: 53(5)46(.)48(0)48(0)-这是为 ASCII,但字符串将包含类似的东西。如果编译器只取第一个 N (4表示双精度?不确定)字节从一个字符串-该块将包含完全不同的双数。 与此同时,Convert.ToDouble ()运行特殊算法,该算法将获取字符串的每个符号,找出它所代表的数字,并为您制作一个双精度数字,如果字符串代表一个数字。 粗略地说,PHP 等语言将在后台为您调用 Convert.ToDouble。但是 C # ,就像静态类型语言一样,不会为您做到这一点。这样可以确保任何操作都是类型安全的,并且不会出现意想不到的情况,比如:

double d = (double)"zzzz"

C # 不允许将字符串转换为类似的 double,这就是为什么会出现 Exception 的原因,您需要将字符串转换(MSDN 文件显示可接受的转换路径)。这仅仅是因为字符串不一定包含数值数据,但是各种数值类型会包含(除非空值)。Convert将运行一个方法,该方法将检查字符串以确定它是否可以转换为数值。如果可以,那么它将返回该值。如果不能,它会抛出一个异常。

要转换它,您有几个选项。你在你的问题中使用了 Convert方法,有一个 Parse,它与 Convert大体相似,但是你也应该看看 尝试解析,它允许你做:

string variable = "5.00";


double varDouble;


if (Double.TryParse(variable, out varDouble)) {
//Code that runs if the conversion succeeded.
} else {
//Code that runs if the conversion failed.
}

如果您尝试使用非数字字符串 ConvertParse,这可以避免可能的异常。

Casting does not involve any conversion, i.e. the internal representation of a value is not changed. Example:

object o = "Hello"; // o is typed as object and contains a string.
string s = (string)o; // This works only if o really contains a string or null.

你可以像这样把 double转换成 string

double d = 5;
string s = d.ToString(); // -> "5"


// Or by specifying a format
string formatted = d.ToString("N2"); // -> "5.00"

你可以通过几种方式将一个 string转换成一个 double(这里只有两种方式) :

string s = "5";
double d = Double.Parse(s); // Throws an exception if s does not contain a valid number

或者安全的方法

string s = "5";
double d;
if (Double.TryParse(s, out d)) {
Console.WriteLine("OK. Result = {0}", d);
} else {
Console.WriteLine("oops!");
}

铸造是告诉编译器的一种方式,“我知道你认为这个变量是一个 Bar,但是我碰巧比你知道的更多; 这个对象实际上是一个 Foo,所以从现在开始让我把它当作一个 Foo。”然后,在运行时,如果实际对象确实是一个 Foo,那么代码就可以工作,如果实际对象根本不是一个 Foo,那么就会出现异常。(特别是 System.InvalidCastException。)

另一方面,转换是一种说法,“如果你给我一个 Bar 类型的对象,我可以创建一个全新的 Foo 对象,表示 Bar 对象中的内容。我不会改变原始对象,它不会区别对待原始对象,它会 创造一些基于其他价值的新东西。至于它将如何做到这一点,它可以是任何东西。在 Convert.ToDouble的情况下,它将最终调用 Double.ParseDouble.Parse具有各种复杂的逻辑,用于确定哪些类型的字符串代表哪些数值。您可以编写自己的转换方法,以不同的方式将字符串映射为双精度(也许是为了支持一些完全不同的数字显示约定,例如罗马数字或其他)。一个转换可以做任何事情,但是这个想法是你不是真的要求编译器为你做任何事情; 你是一个编写代码来确定如何创建新对象的人,因为编译器,没有你的帮助,没有办法知道如何映射(例如)一个 string到一个 double

那么,你什么时候皈依,什么时候施法?在这两种情况下,我们都有一个类型为 A 的变量,我们希望有一个类型为 B 的变量。如果我们的 A 对象实际上在引擎盖下面是 B 那么我们就转换。如果它不是真正的 B,那么我们需要转换它,并定义程序应该如何从 A 得到 B。

即使你把它们看作是等价的,它们在目的上也是完全不同的。让我们首先尝试定义什么是演员阵容:

强制转换是将一种数据类型的实体更改为另一种数据类型的操作。

它有点泛型,在某种程度上等价于 转变,因为强制转换通常具有相同的转换语法,所以问题应该是 什么时候语言允许转换(隐式或显式) ,什么时候需要使用(更多)显式转换?

让我们首先 拔枪他们之间的一个简单的线。形式上(即使对于语言语法来说是等价的)强制转换将改变类型,而转换将改变/可能改变值(最终 一起与类型)。此外,铸造是可逆的,而转换可能不是。

这个话题相当广泛,所以让我们通过排除游戏中的自定义角色转换操作符来缩小一点范围。

隐性石膏

在 C # 中,强制转换是 当你不会丢失任何信息的时候(请注意,这个检查是执行 类型,而不是它们的实际值的)。

Primitive types

例如:

int tinyInteger = 10;
long bigInteger = tinyInteger;


float tinyReal = 10.0f;
double bigReal = tinyReal;

这些强制转换是隐式的,因为在转换过程中您不会丢失任何信息(您只需将类型扩大)。反之亦然,隐式强制转换是不允许的,因为无论它们的实际值如何(因为它们只能在运行时检查) ,在转换过程中您可能会丢失一些信息。例如,这段代码无法编译,因为 double可能包含(实际上确实包含)一个不能用 float表示的值:

// won't compile!
double bigReal = Double.MaxValue;
float tinyReal = bigReal;

物品

对于一个对象(指向它的指针) ,当编译器能够确定源类型是一个派生类(或者它实现了)目标类的类型时,转换总是隐式的,例如:

string text = "123";
IFormattable formattable = text;


NotSupportedException derivedException = new NotSupportedException();
Exception baseException = derivedException;

在这种情况下,编译器 知道string实现 IFormattable,而 NotSupportedException是(派生自) Exception,因此转换是隐式的。没有信息丢失,因为对象不会改变它们的类型(这与 struct和基本类型不同,因为通过强制转换可以创建 另一种类型的新对象) ,改变的是它们的 风景

明确的石膏模型

当编译器没有隐式地完成转换时,转换是显式的,然后您必须使用转换运算符。通常它的意思是:

  • 你可能会丢失信息或数据,所以你必须意识到这一点。
  • 转换可能会失败(因为无法将一种类型转换为另一种类型) ,因此,您必须再次清楚自己在做什么。

原始人

当转换过程中可能丢失某些数据时,需要对基元类型进行显式强制转换,例如:

double precise = Math.Cos(Math.PI * 1.23456) / Math.Sin(1.23456);
float coarse = (float)precise;


float epsilon = (float)Double.Epsilon;

在这两个示例中,即使值在 float范围内,也会丢失信息(在本例中是精度) ,因此转换必须是显式的。现在试试这个:

float max = (float)Double.MaxValue;

This conversion will fail so, again, it must be explicit so you're aware of it and you may do a check (in the example the value is constant but it may come from some run-time computations or I/O). Back to your example:

// won't compile!
string text = "123";
double value = (double)text;

由于编译器无法将文本转换为数字,因此无法编译。文本可能包含任何字符,而不仅仅是数字,这在 C # 中是过多的,即使是对于显式强制转换(但在另一种语言中可能允许)。

物品

如果类型不相关,从指针(到对象)的转换可能会失败,例如这段代码不会编译(因为编译器知道没有可能的转换) :

// won't compile!
string text = (string)AppDomain.Current;
Exception exception = (Exception)"abc";

这段代码将编译,但它可能在运行时失败(这取决于强制转换对象的有效类型) ,InvalidCastException:

object obj = GetNextObjectFromInput();
string text = (string)obj;


obj = GetNextObjectFromInput();
Exception exception = (Exception)obj;

转换

那么,最后,如果强制转换是转换,那么为什么我们需要像 Convert这样的类呢?忽略来自 Convert实现和 IConvertible实现的细微差别,实际上是因为在 C # 中使用强制转换对编译器说:

trust me, this type is that type even if you can't know it now, let me do it and you'll see.

或者

别担心,我不在乎这次转换是否会有所损失。

对于其他任何事情,都需要一个 更多显式操作(想想 简单的石膏的含义,这就是为什么 C + + 为它们引入了冗长、详细和显式的语法)。这可能涉及一个复杂的操作(对于 string-> double转换,需要解析)。例如,到 string的转换总是可能的(通过 ToString()方法) ,但它可能意味着与您期望的不同的东西,因此它必须比强制转换(你写得越多,你就越会思考你在做什么)更明确。

这种转换可以在对象内部完成(使用已知的 IL 指令) ,使用自定义转换运算符(在要强制转换的类中定义)或更复杂的机制(例如,TypeConverter或类方法)。你不知道会发生什么,但你知道它可能会失败(这就是为什么 IMO 时,更多的 控制转换是可能的,你应该使用它)。在您的情况下,转换只是解析 string以产生 double:

double value = Double.Parse(aStringVariable);

Of course this may fail so if you do it you should always catch the exception it may throw (FormatException). It's out of topic here but when a TryParse is available then you should use it (because semantically you it may not be a number and it's even faster...to fail).

转换。NET 可以来自很多地方,如 TypeConverter、使用用户定义的转换操作符的隐式/显式转换、 IConvertible的实现和解析方法(我是不是忘了什么?).有关它们的更多细节,请查看 MSDN。

为了完成这个冗长的答案,只需要说几句关于用户定义的转换运算符的话。只有 Sugar允许程序员使用强制转换将一种类型转换为另一种类型。它是类中的一个方法(将被强制转换的那个类) ,它说“嘿,如果他/她想把这个类型转换成那个类型,那么我可以做到”。例如:

float? maybe = 10; // Equals to Nullable<float> maybe = 10;
float sure1 = (float)maybe; // With cast
float sure2 = maybe.Value; // Without cast

在这种情况下,它是显式的,因为它可能会失败,但这是让实现(即使有关于这方面的准则)。假设您编写一个这样的自定义字符串类:

EasyString text = "123"; // Implicit from string
double value = (string)text; // Explicit to double

在您的实现中,您可能决定“使程序员的生活更容易”,并通过强制转换公开此转换(请记住,这只是编写更少内容的快捷方式)。有些语言甚至允许这样做:

double value = "123";

允许隐式转换为任何类型(检查将在运行时完成)。使用适当的选项,这可以完成,例如,在 VB.NET 中。只是理念不同罢了。

我能用它们做什么?

所以最后一个问题是什么时候应该使用这个或那个。让我们看看什么时候可以使用显式强制转换:

  • 基类型之间的转换。
  • Conversions from object to any other type (this may include unboxing too).
  • 从派生类到基类(或实现的接口)的转换。
  • 通过自定义转换运算符从一种类型转换为另一种类型。

只有第一个转换可以用 Convert完成,因此对于其他的转换,您没有选择,您需要使用显式强制转换。

现在让我们看看什么时候可以使用 Convert:

  • 从任何基类型到另一个基类型的转换(有一些限制,请参阅 MSDN)。
  • 从实现 IConvertible的任何类型到任何其他(受支持)类型的转换。
  • 从/到 byte数组到/从字符串的转换。

Conclusions

IMO Convert should be used each time you know a conversion may fail (because of the format, because of the range or because it may be unsupported), even if the same conversion can be done with a cast (unless something else is available). 它清楚地表明谁将读取您的代码,您的意图是什么 and that it may fail (simplifying debug).

For everything else you need to use a cast, no choice, but if another better method is available then I suggest you use it. In your example a conversion from string to double is something that (especially if text comes from user) very often will fail so you should make it as much explicit as possible (moreover you get more control over it), for example using a TryParse method.

Edit: what's the difference between them?

根据最新的问题,并保留我之前写的内容(关于 什么时候,你可以使用强制转换,相比之下,你可以/必须使用 Convert) ,那么最后一点需要澄清的是,它们之间是否存在差异(此外,Convert使用 IConvertibleIFormattable接口,因此它可以执行不允许使用强制转换的操作)。

简短的回答是 是的,他们的行为不同。我将 Convert类看作一个助手方法类,因此它经常提供一些 利益或略有不同的行为。例如:

double real = 1.6;
int castedInteger = (int)real; // 1
int convertedInteger = Convert.ToInt32(real); // 2

很不一样,对吧?强制转换截断(这是我们都期望的) ,但是 Convert执行一个舍入到最接近的整数(如果您没有意识到这一点,可能不会期望这样做)。每种转换方法都会引入差异,因此不能应用一般规则,它们必须一个案例一个案例地看... 19个基本类型可以转换成其他类型... 列表可以非常长,最好一个案例一个案例地参考 MSDN!

double varDouble = (double)variable假设 variable已经是一个 double。如果 variable不是 double (它是一个字符串) ,那么这将失败。 double varDouble = Convert.ToDouble(variable)就像它说的那样,它是 改变信仰。如果它能够解析或者从 variable中提取一个双精度数据,那么它就会。

I second using Double.Parse or Double.TryParse because it more clearly indicates what's supposed to be happening. You're starting with a string and expecting it to be convertible to a double. If there's any doubt, use TryParse.

If variable is a method argument, change the type to double. Make the caller responsible for providing the correct type. That way the compiler does the work for you.

来自 MSDN:

显式转换(强制转换) : 显式转换需要强制转换运算符。需要进行转换时,转换过程中可能会丢失 什么时候信息,或者由于其他原因转换可能无法成功。典型示例包括将数值转换为精度较低或范围较小的类型,以及将基类实例转换为派生类。

考虑下面的例子:

double a = 2548.3;
int b;
b = (int)a; //2548 --> information (.3) lost in the conversion

还有:

强制转换是一种显式通知编译器您打算进行转换并意识到可能发生数据丢失的方法。

当需要在 non-compatible类型之间进行转换时,可以使用 System.Convert类。选角convert之间的 主要区别编译运行时间。类型转换异常出现在 运行时间,即在运行时失败的类型强制转换将引发 InvalidCastException


Conclusion: In casting you are telling to the compiler that a is really type b and if so the project builds without any errors like this example:

double s = 2;
int a = (int) s;

But in conversion you're saying to the compiler there is a way to create a new object from a of type b, please do it and project builds without any errors but as I said if type cast fails at run-time, it will cause an InvalidCastException to be thrown.

例如,由于编译器检测到不能将 DateTime类型的表达式强制转换为 int类型,因此下面的代码永远不会被编译:

DateTime s = DateTime.Now;
int a = (int)(s);

但是这个是成功编译的:

DateTime s = DateTime.Now;
int a = Convert.ToInt32(s);

但是在运行时,你会得到 InvalidCastException,它说:

从“ DateTime”到“ Int32”的强制转换无效。

最重要的区别是,如果使用 类型铸造并且转换失败 (假设我们正在将一个非常大的 float 值转换为 int)不会抛出任何异常,并且将显示 int 可以保存的最小值。 但是在使用 改变信仰的情况下,将为此类场景引发异常。