转换和使用 Convert.To() 方法之间的区别 [英] Difference between casting and using the Convert.To() method

查看:19
本文介绍了转换和使用 Convert.To() 方法之间的区别的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个函数可以在 string 值上转换一个 double.

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

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

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

string 变量 = "5.00";double varDouble = Convert.ToDouble(变量);

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

转换和使用 Convert.To() 方法有什么区别? 为什么转换会抛出 Exception 和使用 Convert.To() 没有?

解决方案

即使您可能认为它们以某种方式等效,但它们的用途却完全不同.让我们首先尝试定义演员表是什么:

<块引用>

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

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

让我先在它们之间一条简单的线.正式地(即使对于语言语法等效),强制转换将更改类型,而转换将/可能会更改值(最终 与类型一起).此外,转换是可逆的,而转换可能不可逆.

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

隐式转换

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

原始类型

例如:

int tinyInteger = 10;long bigInteger = tinyInteger;浮动 tinyReal = 10.0f;双 bigReal = tinyReal;

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

//不会编译!double bigReal = Double.MaxValue;浮动 tinyReal = bigReal;

对象

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

string text = "123";IFormattable formattable = text;NotSupportedExceptionderivedException = new NotSupportedException();异常 baseException = 派生异常;

在这种情况下,编译器知道 string 实现了 IFormattable 并且 NotSupportedException 是(派生自)Exception 所以转换是隐式的.不会丢失任何信息,因为对象不会改变它们的类型(这与 struct 和原始类型不同,因为使用强制转换您可以创建另一种类型的新对象),您对它们的看法有何变化.

显式转换

当转换不是由编译器隐式完成时,转换是显式的,然后您必须使用转换运算符.通常意味着:

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

原始类型

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

double precision = Math.Cos(Math.PI * 1.23456)/Math.Sin(1.23456);浮动粗=(浮动)精确;float epsilon = (float)Double.Epsilon;

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

float max = (float)Double.MaxValue;

这个转换会失败,所以,再次,它必须是显式的,所以你知道它,你可以做一个检查(在这个例子中,值是常数,但它可能来自一些运行时计算或 I/O).回到你的例子:

//不会编译!字符串文本=123";双值=(双)文本;

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

对象

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

//不会编译!string text = (string)AppDomain.Current;异常exception = (Exception)abc";

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

object obj = GetNextObjectFromInput();字符串文本=(字符串)obj;obj = GetNextObjectFromInput();异常异常 = (Exception)obj;

转化次数

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

<块引用>

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

-或-

<块引用>

别担心,我不在乎这次转换会不会有什么损失.

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

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

double value = Double.Parse(aStringVariable);

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

.NET 中的转换可以来自很多地方,TypeConverter、使用用户定义的转换运算符的隐式/显式转换、IConvertible 的实现和解析方法(我忘记了什么?).有关它们的更多详细信息,请查看 MSDN.

为了完成这个长答案,只需简单介绍一下用户定义的转换运算符.让程序员使用强制转换将一种类型转换为另一种类型只是.这是一个类(将被强制转换的那个)中的一个方法,它说嘿,如果他/她想将这种类型转换为那种类型,那么我可以做到".例如:

浮动?也许 = 10;//等于 Nullable也许 = 10;浮动确定1 =(浮动)也许;//带演员表浮动确定2 =也许.价值;//不带演员表

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

EasyString text = "123";//来自字符串的隐式双值=(字符串)文本;//显式加倍

在您的实现中,您可能决定让程序员的生活更轻松";并通过强制转换公开此转换(请记住,这只是减少编写的快捷方式).某些语言甚至可能允许这样做:

double value = "123";

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

我可以用它们做什么?

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

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

使用 Convert 只能完成第一次转换,因此对于其他转换,您别无选择,您需要使用显式转换.

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

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

结论

IMO Convert 每次您知道转换可能会失败(因为格式、范围或因为它可能不受支持)时都应该使用,即使可以使用演员表(除非有其他可用的东西).它明确了谁将阅读您的代码您的意图是什么并且它可能会失败(简化调试).

对于其他所有需要使用演员表的事情,别无选择,但如果有其他更好的方法可用,那么我建议您使用它.在您的示例中,从 stringdouble 的转换(特别是如果文本来自用户)经常会失败,因此您应该使其尽可能明确(此外您可以更好地控制它),例如使用 TryParse 方法.

它们之间有什么区别?

根据更新的问题并保留我之前写的内容(关于何时,与何时可以/必须使用Convert相比,您可以使用强制转换)然后最后指向澄清一下它们之间是否有区别(此外,Convert 使用 IConvertibleIFormattable 接口,因此它可以执行强制转换不允许的操作).

简短的回答是是的,他们的行为不同.我认为 Convert 类就像一个辅助方法类,所以它经常提供一些好处或稍微不同的行为.例如:

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

很不一样吧?强制转换会截断(这是我们都期望的),但 Convert 执行舍入到最接近的整数(如果您不知道,这可能不会发生).每种转换方法都引入了差异,因此不能应用一般规则,必须逐案查看……19 种基本类型转换为其他所有类型……列表可能很长,最好通过 MSDN 案例咨询案例!

I have a function that casts a double on string values.

string variable = "5.00"; 

double varDouble = (double)variable;

A code change was checked in and the project builds with the error: System.InvalidCastException: Specified cast is not valid.

However, after doing the following...

string variable = "5.00"; 

double varDouble = Convert.ToDouble(variable);

...the project builds without any errors.

What is the difference between casting and using the Convert.To() method? Why does casting throw an Exception and using the Convert.To() does not?

解决方案

Even if you may see them somehow as equivalent they're completely different in purpose. Let's first try to define what a cast is:

Casting is the action of changing an entity of one data type into another.

It's a little bit generic and it's somehow equivalent to a conversion because a cast often has the same syntax of a conversion so the question should be when a cast (implicit or explicit) is allowed by the language and when do you have to use a (more) explicit conversion?

Let me first draw a simple line between them. Formally (even if equivalent for language syntax) a cast will change the type while a conversion will/may change the value (eventually together with the type). Also a cast is reversible while a conversion may not be.

This topic is pretty vast so let's try to narrow it a little bit by excluding custom cast operators from the game.

Implicit casts

In C# a cast is implicit when you won't lose any information (please note that this check is performed with types and not with their actual values).

Primitive types

For example:

int tinyInteger = 10;
long bigInteger = tinyInteger;

float tinyReal = 10.0f;
double bigReal = tinyReal;

These casts are implicit because during the conversion you won't lose any information (you just make the type wider). Vice versa implicit cast isn't allowed because, regardless of their actual values (because they can be checked only at run-time), during the conversion you may lose some information. For example this code won't compile because a double may contain (and actually it does) a value not representable with a float:

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

Objects

In case of an object (a pointer to) the cast is always implicit when the compiler can be sure that the source type is a derived class (or it implements) the type of the target class, for example:

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

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

In this case the compiler knows that string implements IFormattable and that NotSupportedException is (derives from) Exception so the cast is implicit. No information is lost because objects don't change their types (this is different with structs and primitive types because with a cast you create a new object of another type), what changes is your view of them.

Explicit casts

A cast is explicit when the conversion isn't done implicitly by the compiler and then you must use the cast operator. Usually it means that:

  • You may lose information or data so you have to be aware of it.
  • The conversion may fail (because you can't convert one type to the other) so, again, you must be aware of what you're doing.

Primitive types

An explicit cast is required for primitive types when during the conversion you may lose some data, for example:

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

float epsilon = (float)Double.Epsilon;

In both examples, even if the values fall within the float range, you'll lose information (in this case precision) so the conversion must be explicit. Now try this:

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;

This won't compile because the compiler can't convert text to numbers. Text may contain any characters, not numbers only and this is too much, in C#, even for an explicit cast (but it may be allowed in another language).

Objects

Conversions from pointers (to objects) may fail if the types are unrelated, for example this code won't compile (because the compiler knows there is no possible conversion):

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

This code will compile but it may fail at run-time (it depends on the effective type of casted objects) with an InvalidCastException:

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

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

Conversions

So, finally, if casts are conversions then why do we need classes like Convert? Ignoring the subtle differences that come from Convert implementation and IConvertible implementations actually because in C# with a cast you say to the compiler:

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

-or-

don't worry, I don't care if something will be lost in this conversion.

For anything else a more explicit operation is needed (think about implications of easy casts, that's why C++ introduced long, verbose and explicit syntax for them). This may involve a complex operation (for string -> double conversion a parsing will be needed). A conversion to string, for example, is always possible (via ToString() method) but it may mean something different from what you expect so it must be more explicit than a cast (more you write, more you think about what you're doing).

This conversion can be done inside the object (using known IL instructions for that), using custom conversion operators (defined in the class to cast) or more complex mechanisms (TypeConverters or class methods, for example). You're not aware of what will happen to do that but you're aware it may fail (that's why IMO when a more controlled conversion is possible you should use it). In your case the conversion simply will parse the string to produce a 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 say it may not be a number and it's even faster...to fail).

Conversions in .NET can come from a lot of places, TypeConverter, implicit/explicit casts with user defined conversion operators, implementation of IConvertible and parsing methods (did I forget something?). Take a look on MSDN for more details about them.

To finish this long answer just few words about user defined conversion operators. It's just sugar to let the programmer use a cast to convert one type to another. It's a method inside a class (the one that will be casted) that says "hey, if he/she wants to convert this type to that type then I can do it". For example:

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

In this case it's explicit because it may fail but this is let to the implementation (even if there are guidelines about this). Imagine you write a custom string class like this:

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

In your implementation you may decide to "make programmer's life easier" and to expose this conversion via a cast (remember it's just a shortcut to write less). Some language may even allow this:

double value = "123";

Allowing implicit conversion to any type (check will be done at run-time). With proper options this can be done, for example, in VB.NET. It's just a different philosophy.

What can I do with them?

So the final question is when you should use one or another. Let's see when you can use an explicit cast:

  • Conversions between base types.
  • Conversions from object to any other type (this may include unboxing too).
  • Conversions from a derived class to a base class (or to an implemented interface).
  • Conversions from one type to another via custom conversion operators.

Only the first conversion can be done with Convert so for the others you have no choice and you need to use an explicit cast.

Let's see now when you can use Convert:

  • Conversions from any base type to another base type (with some limitations, see MSDN).
  • Conversions from any type that implements IConvertible to any other (supported) type.
  • Conversions from/to a byte array to/from a string.

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). It makes clear to who will read your code what's your intent 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?

According to updated question and keeping what I wrote before (about when you can use a cast compared to when you can/have to use Convert) then last point to clarify is if there are difference between them (moreover Convert uses IConvertible and IFormattable interfaces so it can perform operations not allowed with casts).

Short answer is yes, they behave differently. I see the Convert class like a helper methods class so often it provides some benefit or slightly different behaviors. For example:

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

Pretty different, right? The cast truncates (it's what we all expect) but Convert performs a rounding to nearest integer (and this may not be expected if you're not aware of it). Each conversion method introduces differences so a general rule can't be applied and they must be seen case by case...19 base types to convert to every other type...list can be pretty long, much better to consult MSDN case by case!

这篇关于转换和使用 Convert.To() 方法之间的区别的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆