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


87

我有一个doublestring值进行强制转换的函数。

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()不?

c#  casting 


6
关于引用的问题,OP询问何时使用强制转换或转换,并且接受的答案指出:“这实际上是您选择使用任何一个问题。” 我要求转换与转换之间的区别。在我看来,下面的答案(很抱歉!)提供了有关差异与“根据选择使用此或那个”的更多详细信息……从本质上讲,该详细信息可用于做出更明智的选择。

@ edmastermind29在编程上下文中,“ x和y之间的区别是什么”与“何时使用x和y”之间没有太大区别。双方互相回答。
nawfal

2
大约3年后,在这种情况下似乎没有一个可以相互回答。问:“ X和Y有什么区别?” 答:“无论使用哪种方式,这实际上都是一个选择问题。” 不是很有用。

似乎没有人直接回答“最佳性能”也是问题的一部分,根据我的经验,我发现Cast更好,尤其是在获取像这样的列值时。.(int)datatable.Rows [0] [0],如果我们知道它的100%整型
Sundara Prabu

Answers:


126

即使您可能以某种方式将它们视为等效,它们的目的也完全不同。让我们首先尝试定义什么是强制类型转换:

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

它有点通用,某种程度上等效于转换,因为强制转换通常具有与转换相同的语法,因此问题应该是语言允许使用强制转换(隐式还是显式),以及何时必须使用(更多)显式转换?

首先让我在它们之间一条简单的线。形式上(即使语言语法等效),转换将更改类型,而转换将/可能更改值(最终与类型一起更改)。同样,转换可能是可逆的,而转换可能不是。

这个主题非常广泛,因此让我们通过从游戏中排除自定义强制转换运算符来缩小范围。

隐式转换

在C#中,当您不会丢失任何信息时,强制转换是隐式的(请注意,此检查是针对类型而不是其实际值执行的)。

原始类型

例如:

int tinyInteger = 10;
long bigInteger = tinyInteger;

float tinyReal = 10.0f;
double bigReal = tinyReal;

这些转换是隐式的,因为在转换过程中,您不会丢失任何信息(只需将类型变大)。反之亦然,不允许进行隐式强制转换,因为无论其实际值如何(因为只能在运行时检查它们),转换期间您可能会丢失一些信息。例如,此代码将不会编译,因为adouble可能包含(实际上包含)一个用a不能表示的值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工具IFormattableNotSupportedException是(派生自)Exception这样的转换是隐含的。因为对象不会更改其类型,所以不会丢失任何信息(这与structs和基本类型不同,因为使用强制类型转换您可以创建另一种类型新对象),因此,视图的变化是什么的。

明确的演员表

当编译器未隐式完成转换时,强制转换是显式的,然后必须使用强制转换运算符。通常这意味着:

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

原始类型

当在转换过程中您可能会丢失一些数据时,原始类型需要显式强制转换,例如:

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;

该转换将失败,因此,再次,它必须是显式的,以便您知道它并且可以进行检查(在该示例中,该值是恒定的,但它可能来自某些运行时计算或I / O)。回到您的示例:

// 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#中使用强制转换对编译器说:

相信我,即使您现在不知道这种类型,也要让我这样做,您会看到的。

-要么-

不用担心,我不在乎此转换是否会丢失某些内容。

对于其他任何事情,都需要明确的操作(考虑简单强制转换的含义,这就是C ++为它们引入冗长,冗长和明确的语法的原因)。这可能涉及复杂的操作(对于string->double转换,将需要解析)。string例如,始终可以(通过ToString()方法)转换为,但这可能意味着与您期望的有所不同,因此它必须比强制转换更明确(您写的越多,对您正在做的事情的思考就越多)。

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

double value = Double.Parse(aStringVariable);

当然,这可能会失败,因此,如果执行此操作,则应始终捕获它可能引发的异常(FormatException)。这里没有主题,但是当aTryParse可用时,您应该使用它(因为从语义上它可能不是一个数字,它甚至更快......失败)。

.NET中的转换可能来自很多地方,TypeConverter包括使用用户定义的转换运算符进行隐式/显式强制转换,实现IConvertible和解析方法(我是否忘记了什么?)。查看有关它们的更多详细信息,在MSDN上。

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

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中完成。这只是一种不同的哲学。

我该怎么办?

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

  • 基本类型之间的转换。
  • 的转换 object为其他任何类型(也可能包括取消装箱)。
  • 从派生类到基类(或实现的接口)的转换。
  • 通过自定义转换运算符将一种类型转换为另一种类型。

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

现在让我们看看何时可以使用Convert

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

结论

Convert每次您知道转换可能会失败(由于格式,由于范围或因为它不受支持)而应使用IMO ,即使可以使用强制转换进行相同的转换(除非有其他可用的方法)。它清楚地表明谁将读取您的代码,您的意图是什么,它可能会失败(简化调试)。

对于其他所有内容,您都需要使用强制转换,别无选择,但是如果有其他更好的方法可用,那么我建议您使用它。在您的示例中,从string到的转换double通常会失败(特别是如果文本来自用户),因此您应使其尽可能明确(此外,您可以对其进行更多控制),例如使用一种TryParse方法。

编辑:它们之间有什么区别?

根据更新后的问题并保留我之前写的内容(关于何时可以使用强制转换与何时可以使用/必须使用Convert),最后要澄清的是它们之间是否存在差异(此外,Convert用法IConvertibleIFormattable接口也可以执行操作)不允许进行强制转换)。

简短的回答是,他们的行为有所不同。我将Convert类视为辅助方法类,因此经常提供一些好处或行为略有不同。例如:

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

完全不同,对不对?强制转换会截断(这是我们所有人都希望的),但是Convert会四舍五入到最接近的整数(如果您不知道,则可能无法预期)。每种转换方法都会引入差异,因此无法应用一般规则,必须逐例查看...将19种基本类型转换为其他类型...列表可能会很长,因此最好通过案件!


我改变了要问的问题Difference between casting and using the Convert.To() method。否则,答案很全面。(我希望我的问题能重新打开...)

@ edmastermind29我编辑了一点问题,主题太长,即使答案很长(列表可能有300多个转换)。转换不仅对强制类型转换而且对“普通” IConvertible和IFormattable接口也都带来了好处(或者仅仅是意外行为?)。
Adriano Repetti 2013年

我不喜欢从C借来的概念,即double不代表整数的值应“转换为” int。一铸造似乎在例如,一个被检索的情况下适当的模式Int32,从一个价值double[]持有实数和的混合Int32已经转换到值double[尝试转换的值是不能表示正是int32将指示意外情况并应触发一个例外],但我认为,当有人希望进行有损转换时,应该具体说明自己想要的形式。
2013年

1
另一个区别是从对象到基本类型。例如object o = 123; var l = Convert.ToInt64(o); var i = (long) (int) o; var f = (long) o // InvalidCastException
yue shi

1
@ rory.ap这很重要。不,从形式上讲,这不是强制转换float-> int),而是强制转换。例如,强制转换可能是DerivedClass-> BaseClass。这是令人困惑的,因为在C#中,我们对两者使用相同的词(和运算符),但实际上它们是不同的东西。区分它们的正式定义比我写的要复杂得多。
阿德里亚诺·雷佩蒂

12

强制转换是一种告诉编译器的方式,“我知道您认为此变量是Bar,但是我碰巧知道的比您还多;该对象实际上是Foo,所以让我将其视为来自从今起。” 然后,在运行时,如果实际对象实际上是Foo,则您的代码有效,如果发现该对象根本不是Foo,则您将获得异常。(特别是一个System.InvalidCastException。)

另一方面,转换是一种表达方式,“如果您给我一个Bar类型的对象,我可以创建一个全新的Foo对象,该对象表示该Bar对象中的内容。我不会更改原始对象,它会的。”如果对原始对象进行不同的处理,它将创建一些基于其他值的新内容。关于如何做到这一点,它可以是任何东西。Convert.ToDouble这将最终调用Double.Parse它具有各种复杂的逻辑,用于确定什么类型的字符串代表什么数值。您可以编写自己的转换方法,该方法将字符串映射为不同的双精度字符(也许支持某些完全不同的约定以显示数字,例如罗马数字或其他数字)。转换可以做任何事情,但想法是您并不是在真正要求编译器为您做任何事;而是您可以执行任何操作。您是编写代码以确定如何创建新对象的人,因为在没有您帮助的情况下,编译器无法知道如何将a映射(例如)string到a double

那么,您何时转换,何时转换?在这两种情况下,我们都有一个类型为A的变量,并且我们希望有一个类型为B的变量。如果实际上我们的A对象实际上是一个B,则我们进行转换。如果不是真的B,则需要对其进行转换,并定义程序应如何从A获得B。


SO发表的一篇文章中,埃里克•利珀特(Eric Lippert)提到没有所谓的式转换 ,而是隐式转换。我一直在交替使用强制转换和转换。说“隐式演员表”有什么问题?如果转换是隐式的而不需要任何强制转换,那么可以说它是“隐式强制转换”吗?
rahulaga_dev

1
@RahulAgarwal什么铸造在您需要的操作明确指示给定类型(或可制成)另一种类型的有效实例。当存在隐式转换时,不需要强制类型转换即可将该类型视为另一种类型。因此说“隐式强制转换”实际上没有任何意义(除了可能出现的少数情况,例如Eric提到的情况,其中添加了强制转换运算符而开发人员无需键入它,例如使用时foreach)。除了这些例外,强制转换在定义上是明确的。
Servy

5

来自MSDN

显式转换(广播):显式转换需要强制转换运算符。铸造需要可能会在转换过程中丢失的信息,或当转换可能不会因其他原因取得成功。典型示例包括将数值转换为精度较低或范围较小的类型,以及将基类实例转换为派生类。

考虑以下示例:

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

并且:

强制转换是一种明确地通知编译器您打算进行转换并且知道可能会发生数据丢失的方法。

如果System.Convert要在不兼容的类型之间进行转换,可以使用class 。转换转换之间的主要区别编译运行时。类型转换异常在运行时出现,即,在运行时失败的类型转换将引发抛出。 InvalidCastException


结论:在强制转换中,您正在告诉编译器a真正的类型b,如果是,则该项目的构建不会出现任何错误,例如以下示例:

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

但在转换你说的编译器有是创建一个新的对象的方式a类型b,请做到这一点,项目建立没有任何错误,但正如我所说,如果类型转换在运行时出现故障,它会导致InvalidCastException对被抛出

例如,下面的代码永远不会编译,因为编译器检测到无法将type的类型转换DateTime为type int

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

但这是成功编译的:

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

但是在运行时,您会看到以下InvalidCastException内容:

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


4

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

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

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


1
并且这与显式强制转换有何不同?

3
显式强制转换不查看数据类型是什么,仅查看字节。一个示例将char x ='1'转换为整数,该整数将是49,因为acii表中的字符'1'是#49
user1751547

@ user1751547那么,用户Convert.ToDouble()会超越字节查看数据吗?

@ user1751547我认为这是正确回答此问题所需的直觉。仅仅说“它没有定义”是有点争议的。
Ant P

@ edmastermind29是的,它将查看输入类型,如果它是一个字符串,它将遍历每个字符,并知道如果char的ascii值为49,则它是'1'字符并将其正确转换
user1751547

3

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

要进行工作,需要进行显式转换。

而且我必须指出,您本可以使用Convert.ToDouble而不是,Convert.ToInt64因为当您转换为int时,可能会丢失double值的小数部分。

如果您的变量的值为“ 5.25”,则varDouble将为5.00(由于转换为Int64而损失0.25)

回答有关转换与转换的问题。

您的演员表(明确的演员表)不符合明确的演员表的要求。您尝试使用强制转换运算符强制转换的值无效(即非整数)。

访问此MSDN页面以了解转换/转换规则


@ edmastermind29我已经更新了答案。我希望它能回答您的问题。
Scartag 2013年

与我的问题相关的显式演员表有哪些要求?关于“非整数”值吗?

@ edmastermind29是的。如果您尝试转换为数字类型的值是非数字,则转换无效。需要进行转换。
scartag 2013年

3

强制转换不涉及任何转换,即,值的内部表示不变。例:

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.

你可以转换doublestring喜欢这个

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

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

您可以通过几种方式将a转换string为a 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!");
}

Convert.ToDouble()内部通话Double.Parse()。使用Convert.ToDouble()过度Double.Parse()与否对我有利,为什么?

Convert.ToDouble有很多接受不同类型输入的重载。如果传递了字符串,则过载接受string返回。除此之外,我看不到使用它的优势。0.0null
Olivier Jacot-Descombes 2013年

所以,或者...或者Double.Parse()有什么值得我考虑的地方吗?

Double.Parse()Convert.ToDouble()。更直接。如果您确定您的字符串将包含有效数字,则可以安全地使用它,否则,我建议您使用Double.TryParse
Olivier Jacot-Descombes 2013年

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

语言根本不允许上述转换。这是数字类型的显式转换的列表:http : //msdn.microsoft.com/zh-cn/library/yht2cx7b.aspx如您所见,即使不是每个数字类型都可以转换为另一个数字类型

有关此处投射的更多信息

这与Convert.ToDouble()有何不同?

强制转换类型时,数据结构不会更改。好吧,在进行数值转换的情况下,您可能会丢失一些位或获得一些额外的0位。但是您仍在使用数字。您只是在更改该数字占用的内存量。对于编译器执行所需的一切,这是足够安全的。

但是,当您尝试将字符串转换为数字时,您将无法执行此操作,因为这不足以更改变量占用的内存量。例如,5.00因为字符串是“数字”序列:53(5)46(。)48(0)48(0)-用于ASCII,但是字符串将包含类似的内容。如果编译器仅从字符串中获取前N个(4代表双精度?),则该字节将包含完全不同的双精度数字。同时,Convert.ToDouble()运行特殊的算法,该算法将获取字符串的每个符号,找出它代表的数字,并在字符串代表数字的情况下为您创建一个双精度数字。粗略地说,像PHP这样的语言会在后台为您调用Convert.ToDouble。但是C#就像静态类型的语言一样,不会为您做到这一点。这使您可以确保任何操作都是类型安全的,并且执行以下操作不会导致意外:

double d = (double)"zzzz"

@ edmastermind29看到我更新的答案。我试图解释它。解释远非完美,但假定它可以解释差异。
维克多·

1

不允许将字符串强制转换为类似double的C#,这就是为什么您会得到Exception的原因,您需要转换字符串(MSDN文档中显示可接受的转换路径)。这仅仅是因为字符串不一定包含数字数据,而是各种数字类型(不包含null值)。AConvert将运行一个方法,该方法将检查字符串以查看是否可以将其转换为数字值。如果可以,它将返回该值。如果不能,它将抛出异常。

要转换它,您有几种选择。您Convert在问题中使用了该方法,该方法与Parse大致相似Convert,但是您还应该查看TryParse,它可以让您执行以下操作:

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.
}

这样就避免了可能的例外是你应该尝试Convert或者Parse非数字的字符串。


使用TryParseoverConvert因为TryParse检查转换是否成功对我有利?

@ edmastermind29我想是的。如果转换失败,转换将引发异常。TryParse将返回一个布尔值,如果转换成功,则返回True;如果转换失败,则返回False。
Keen

1

double varDouble = (double)variable假定variable已经是两倍。如果variable不是双精度数(是字符串),则此操作将失败。 double varDouble = Convert.ToDouble(variable)确实像它说的那样-它会转换。如果它可以解析或以其他方式提取双精度值,variable则它将。

我第二次使用Double.Parse或,Double.TryParse因为它可以更清楚地指示应该发生的情况。您从一个字符串开始,期望它可以转换为双精度型。如有疑问,请使用TryParse

如果variable是方法参数,请将类型更改为double。让呼叫者负责提供正确的类型。这样,编译器可以为您完成工作。


-1

最重要的区别是,如果使用类型转换并且转换失败(例如,我们将一个非常大的float值转换为int),则不会引发异常,并且将显示int可以容纳的最小值。但是在使用Convert的情况下,此类情况将引发异常。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.