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

74 c# casting

我有蒙上了功能double上的string价值.

string variable = "5.00"; 

double varDouble = (double)variable;
Run Code Online (Sandbox Code Playgroud)

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

但是,在执行以下操作后......

string variable = "5.00"; 

double varDouble = Convert.ToDouble(variable);
Run Code Online (Sandbox Code Playgroud)

......项目构建没有任何错误.

铸造和使用该Convert.To()方法有什么区别?为什么铸造投掷Exception并使用Convert.To()不?

Adr*_*tti 104

即使你可能会以某种方式看待它们,它们的目的也完全不同.让我们首先尝试定义一个演员是什么:

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

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

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

这个话题非常广泛,所以让我们尝试通过从游戏中排除自定义强制转换运算符来缩小范围.

隐含的演员

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

原始类型

例如:

int tinyInteger = 10;
long bigInteger = tinyInteger;

float tinyReal = 10.0f;
double bigReal = tinyReal;
Run Code Online (Sandbox Code Playgroud)

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

// won't compile!
double bigReal = Double.MaxValue;
float tinyReal = bigReal;
Run Code Online (Sandbox Code Playgroud)

对象

在对象(指向)的情况下,当编译器可以确定源类型是派生类(或它实现)目标类的类型时,强制转换总是隐式的,例如:

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

NotSupportedException derivedException = new NotSupportedException();
Exception baseException = derivedException;
Run Code Online (Sandbox Code Playgroud)

在这种情况下,编译器知道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;
Run Code Online (Sandbox Code Playgroud)

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

float max = (float)Double.MaxValue;
Run Code Online (Sandbox Code Playgroud)

此转换将失败,因此,它必须是明确的,因此您已经意识到它并且您可以进行检查(在示例值中是常量,但它可能来自某些运行时计算或I/O).回到你的例子:

// won't compile!
string text = "123";
double value = (double)text;
Run Code Online (Sandbox Code Playgroud)

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

对象

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

// won't compile!    
string text = (string)AppDomain.Current;
Exception exception = (Exception)"abc";
Run Code Online (Sandbox Code Playgroud)

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

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

obj = GetNextObjectFromInput();
Exception exception = (Exception)obj;
Run Code Online (Sandbox Code Playgroud)

转换

所以,最后,如果演员阵容是转换,那么为什么我们需要像Convert所需的类?忽略来自Convert实现和IConvertible实现的微妙差异实际上是因为在C#中使用了一个强制转换,你会对编译器说:

相信我,这种类型就是那种类型,即使你现在还不知道,让我这样做,你会看到.

-要么-

别担心,我不在乎这种转换会丢失什么.

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

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

double value = Double.Parse(aStringVariable);
Run Code Online (Sandbox Code Playgroud)

当然这可能会失败,所以如果你这样做,你应该总是捕获它可能抛出的异常(FormatException).它不在这里,但如果a TryParse可用,那么你应该使用它(因为在语义上你说它可能不是一个数字,它甚至更快......失败).

.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
Run Code Online (Sandbox Code Playgroud)

在这种情况下,它是明确的,因为它可能会失败,但这是实现(即使有关于此的指导).想象一下,你编写一个这样的自定义字符串类:

EasyString text = "123"; // Implicit from string
double value = (string)text; // Explicit to double
Run Code Online (Sandbox Code Playgroud)

在您的实现中,您可能决定"让程序员的生活变得更轻松",并通过演员表示这种转换(记住它只是一个写得更少的捷径).某些语言甚至可能允许这样:

double value = "123";
Run Code Online (Sandbox Code Playgroud)

允许隐式转换为任何类型(检查将在运行时完成).有了适当的选项,这可以在VB.NET中完成.这只是一种不同的哲学.

我能用它做什么?

所以最后一个问题是你何时应该使用其中一个.让我们看看何时可以使用显式转换:

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

只有第一次转换才能完成,Convert所以对于其他人你没有选择,你需要使用显式转换.

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

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

结论

Convert每次知道转换可能失败时都应该使用IMO (因为格式,因为范围或者因为它可能不受支持),即使可以使用强制转换进行相同的转换(除非其他可用).它清楚地表明谁将阅读你的代码你的意图是什么,它可能会失败(简化调试).

对于其他一切你需要使用演员,没有选择,但如果有另一种更好的方法,那么我建议你使用它.在您的示例中,转换stringdouble(特别是如果文本来自用户)通常会失败,因此您应该尽可能明确地进行转换(此外,您可以对其进行更多控制),例如使用TryParse方法.

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

根据更新的问题并保留我之前写的内容(关于什么时候你可以使用演员表比你何时可以/必须使用Convert)然后最后一点澄清的是它们之间是否存在差异(此外还有Convert使用IConvertibleIFormattable接口以便它可以执行操作)不允许使用演员阵容).

简短的回答是肯定的,他们表现不同.我认为Convert类就像一个辅助方法类,因此它经常提供一些好处或略有不同的行为.例如:

double real = 1.6;
int castedInteger = (int)real; // 1
int convertedInteger = Convert.ToInt32(real); // 2
Run Code Online (Sandbox Code Playgroud)

很不一样吧?转换截断(这是我们所期望的)但是Convert执行舍入到最接近的整数(如果你不知道它,这可能不会被预期).每种转换方法都会引入差异,因此一般规则无法应用,必须逐个查看... 19种基本类型转换为每种其他类型...列表可能相当长,更好地查询MSDN案例案件!


Ser*_*rvy 12

Casting是告诉编译器的一种方式,"我知道你认为这个变量是一个Bar,但我碰巧知道的比你多;对象实际上是一个Foo,所以让我把它看作是一个Foo来自从今起." 然后,在运行时,如果实际对象真的是一个Foo然后你的代码工作,如果事实证明该对象根本不是Foo,那么你得到一个例外.(特别是System.InvalidCastException.)

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

所以,你什么时候转换,什么时候投票?在这两种情况下,我们都有一个类型的变量,比方说A,我们想要一个B类型的变量.如果我们的A对象真的,实际上,在引擎盖下,是一个B,那么我们就投了.如果它不是真的B,那么我们需要转换它,并定义程序应该如何从A获得B.


Sal*_*ari 6

来自MSDN

Explicit conversions (casts): Explicit conversions require a cast operator. Casting is required when information might be lost in the conversion, or when the conversion might not succeed for other reasons. Typical examples include numeric conversion to a type that has less precision or a smaller range, and conversion of a base-class instance to a derived class.

Consider the following example:

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

And also:

A cast is a way of explicitly informing the compiler that you intend to make the conversion and that you are aware that data loss might occur.

You could use System.Convert class when you want to convert between non-compatible types. The main difference between casting and convert is compile and run-time. The type conversion exceptions are appeared at run-time , i.e a type cast that fails at run-time will cause an InvalidCastException to be thrown.


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;
Run Code Online (Sandbox Code Playgroud)

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.

For example the code below is never compile because compiler detect that cannot cast expression of type DateTime to type int:

DateTime s = DateTime.Now;
int a = (int)(s);
Run Code Online (Sandbox Code Playgroud)

But this one is compiled successfully:

DateTime s = DateTime.Now;
int a = Convert.ToInt32(s);
Run Code Online (Sandbox Code Playgroud)

But at run-time you will get InvalidCastException which says:

Invalid cast from 'DateTime' to 'Int32'.