47

在 Jesse Liberty 的 Learning C# 一书中,他说“一种类型的对象可以转换为另一种类型的对象。这称为强制转换。”

如果您调查从下面的代码生成的 IL,您可以清楚地看到转换后的赋值与转换后的赋值不同。在前者中,您可以看到正在发生的装箱/拆箱;在后者中,您可以看到对转换方法的调用。

我知道最终这可能只是一个愚蠢的语义差异——但只是转换的另一个词。我并不是要刻薄,但我对任何人对此的直觉不感兴趣——意见在这里不重要!谁能指出一个明确的参考来确认或否认铸造和转换是否是同一件事?

    object x;
    int y;

    x = 4;

    y = ( int )x;

    y = Convert.ToInt32( x );

谢谢

rp

在马特关于显式/隐式的评论之后添加的注释:

我不认为隐式/显式是区别。在我发布的代码中,两种情况下的更改都是明确的。将 short 分配给 int 时会发生隐式转换。

给 Sklivvz 的注意事项:

我想确认我对 Jesse Liberty(通常是清晰明了的)语言松散的怀疑是正确的。我认为 Jesse Liberty 对他的语言有点松懈。我知道转换是在对象层次结构中路由的——即,您不能从整数转换为字符串,但可以从派生自 System.Exception 的自定义异常转换为 System.Exception。

不过,有趣的是,当您尝试从 int 转换为字符串时,编译器会告诉您它无法“转换”该值。也许杰西比我想象的更正确!

4

11 回答 11

29

绝对不!

Convert 尝试通过“任何可能的方式”为您提供 Int32。Cast 没有做任何此类事情。使用 cast 您告诉编译器将对象视为 Int,而不进行转换。

当您知道(通过设计)该对象是 Int32 或另一个具有 Int32 转换运算符的类(例如 float)时,您应该始终使用 cast。

Convert 应与 String 或其他类一起使用。

试试这个

static void Main(string[] args)
{
    long l = long.MaxValue;

    Console.WriteLine(l);

    byte b = (byte) l;

    Console.WriteLine(b);

    b = Convert.ToByte(l);

    Console.WriteLine(b);

}

结果:

9223372036854775807

255

未处理的异常:

System.OverflowException:值大于 Byte.MaxValue 或小于 Byte.MinValue 在 System.Convert.ToByte(Int64 值)[0x00000] 在 Test.Main (System.String[] args) [0x00019] 在 /home/marco /develop/test/Exceptions.cs:15

于 2008-09-27T17:03:44.660 回答
18

简单的答案是:视情况而定。

对于值类型,强制转换将涉及真正将其转换为不同的类型。例如:

float f = 1.5f;
int i = (int) f; // Conversion

当转换表达式拆箱时,结果(假设它有效)通常只是盒子中内容的副本,具有相同的类型。然而,也有例外——你可以从一个装箱的 int 拆箱到一个 enum(具有底层的 int 类型),反之亦然;同样,您可以将装箱的 int 拆箱为 Nullable<int>。

当转换表达式从一种引用类型到另一种引用类型并且不涉及用户定义的转换时,就对象本身而言没有转换 - 只有引用的类型“改变” - 这实际上只是value 被视为,而不是引用本身(这将是与以前相同的位)。例如:

object o = "hello";
string x = (string) o; // No data is "converted"; x and o refer to the same object

当涉及到用户定义的转换时,这通常需要返回不同的对象/值。例如,您可以为您自己的类型定义一个到字符串的转换——这肯定不会是与您自己的对象相同的数据。(当然,它可能是已经从您的对象中引用的现有字符串。)根据我的经验,用户定义的转换通常存在于值类型而不是引用类型之间,因此这很少成为问题。

根据规范,所有这些都算作转换——但它们并不都算作将对象转换为不同类型的对象。我怀疑这是 Jesse Liberty 使用术语松散的一个案例——我在 Programming C# 3.0 中注意到了这一点,我刚刚读过。

这涵盖了一切吗?

于 2008-09-27T17:21:33.197 回答
7

我见过的最好的解释可以在下面看到,然后是源链接:

“...事实比这要复杂一些。.NET 提供了三种从 A 点到 B 点的方法。

首先,有隐式演员表。这是一个不需要你做任何事情的演员:

int i = 5;
double d = i;

这些也称为“扩大转换”,.NET 允许您在没有任何强制转换运算符的情况下执行它们,因为这样做您永远不会丢失任何信息:双精度的可能有效值范围包括 int 的有效值范围,然后一些,所以你永远不会去做这个任务,然后你会惊恐地发现运行时会从你的 int 值中删除几位数字。对于引用类型,隐式转换背后的规则是转换永远不会抛出 InvalidCastException:编译器很清楚转换总是有效的。

您可以为自己的类型创建新的隐式转换运算符(这意味着您可以进行隐式转换来破坏所有规则,如果您对此很愚蠢)。基本的经验法则是隐式转换永远不能包括在转换中丢失信息的可能性。

请注意,底层表示在此转换中确实发生了变化:double 的表示方式与 int 完全不同。

第二种转换是显式转换。只要有可能丢失信息,或者转换可能无效并因此抛出 InvalidCastException,就需要显式转换:

double d = 1.5;
int i = (int)d;

在这里,您显然会丢失信息:演员表后 i 将是 1,因此 0.5 会丢失。这也称为“缩小”转换,编译器要求您包含显式转换 (int) 以表明是的,您知道信息可能会丢失,但您不在乎。

同样,对于引用类型,编译器在强制类型转换在运行时可能无效的情况下需要显式强制类型转换,作为一个信号,是的,您知道存在风险,但您知道自己在做什么。

第三种转换涉及到表示形式的巨大变化,以至于设计者甚至没有提供明确的转换:他们让你调用一个方法来进行转换:

string s = "15";
int i = Convert.ToInt32(s);

请注意,这里没有任何东西绝对需要方法调用。隐式和显式转换也是方法调用(这就是您自己制作的方式)。设计者可以很容易地创建一个将字符串转换为 int 的显式转换运算符。调用方法的要求是一种风格选择,而不是语言的基本要求。

文体推理是这样的:String-to-int 是一个复杂的转换,有很多机会出现可怕的错误:

string s = "The quick brown fox";
int i = Convert.ToInt32(s);

因此,方法调用为您提供了可供阅读的文档,并广泛暗示这不仅仅是快速转换。

在设计您自己的类型(尤其是您自己的值类型)时,您可能会决定创建强制转换运算符和转换函数。划分“隐式转换”、“显式转换”和“转换函数”领域的界限有点​​模糊,因此不同的人可能会做出不同的决定来决定什么应该是什么。试着记住信息丢失、异常和无效数据的可能性,这应该有助于你做出决定。”

  • 布鲁斯·伍德,2005 年 11 月 16 日

http://bytes.com/forum/post1068532-4.html

于 2008-09-27T17:12:01.550 回答
2

铸造涉及参考

List<int> myList = new List<int>();
//up-cast
IEnumerable<int> myEnumerable = (IEnumerable<int>) myList;
//down-cast
List<int> myOtherList = (List<int>) myEnumerable;

请注意,针对 myList 的操作(例如添加元素)反映在 myEnumerable 和 myOtherList 中。这是因为它们都是对同一实例的(不同类型的)引用。

向上转换是安全的。如果程序员在类型中犯了错误,向下转换会产生运行时错误。安全向下转换超出了此答案的范围。

转换涉及实例

List<int> myList = new List<int>();
int[] myArray = myList.ToArray();

myList 用于生成 myArray。这是一个非破坏性的转换(myList 在此操作后工作得非常好)。另请注意,针对 myList 的操作(例如添加元素)不会反映在 myArray 中。这是因为它们是完全独立的实例。

decimal w = 1.1m;
int x = (int)w;

在 C# 中有使用强制转换语法的操作实际上是转换

于 2008-09-27T17:40:12.857 回答
1

除了语义,快速测试表明它们不等价!
他们以不同的方式完成任务(或者,他们可能执行不同的任务)。

x=-2.5 (int)x=-2 Convert.ToInt32(x)=-2
x=-1.5 (int)x=-1 Convert.ToInt32(x)=-2
x=-0.5 (int)x= 0 Convert.ToInt32(x)= 0
x= 0.5 (int)x= 0 Convert.ToInt32(x)= 0
x= 1.5 (int)x= 1 Convert.ToInt32(x)= 2
x= 2.5 (int)x= 2 Convert.ToInt32(x)= 2

注意x=-1.5x=1.5情况。

于 2008-10-29T13:49:36.467 回答
0

强制转换告诉编译器/解释器该对象实际上属于该类型(或具有该类型的基本类型/接口)。与转换相比,这是一件相当快的事情,转换不再是编译器/解释器在做这项工作,而是一个函数实际解析字符串并进行数学运算以转换​​为数字。

于 2008-09-27T17:04:41.037 回答
0

强制转换总是意味着改变对象的数据类型。这可以通过例如将浮点值转换为整数值或通过重新解释位来完成。它通常是一种语言支持(阅读:编译器支持)的操作。

术语“转换”有时用于强制转换,但它通常由某个库或您自己的代码完成,不一定与强制转换相同。例如,如果您有一个英制重量值并将其转换为公制重量,它可能会保持相同的数据类型(例如浮点数),但会变成不同的数字。另一个典型的例子是从度到弧度的转换。

于 2008-09-27T17:06:24.607 回答
0

在与语言/框架无关的说话方式中,从一种类型或类转换为另一种称为强制转换。.NET 也是如此,正如您的前四行所示:

object x;
int y;

x = 4;

y = ( int )x;

C 和类 C 语言(例如 C#)使用(newtype)somevar语法进行转换。例如,在 VB.NET 中有明确的内置函数。最后一行将写为:

y = CInt(x)

或者,对于更复杂的类型:

y = CType(x, newtype)

其中'C'显然是'cast'的缩写。

然而, .NET 也有这个Convert()功能。这不是内置的语言功能(与上述两个不同),而是框架之一。当您使用一种不一定与 .NET 一起使用的语言时,这一点会变得更清楚:它们仍然很可能有自己的转换方式,但 .NET 增加了Convert().

正如马特所说,行为上的差异Convert()更加明确。而不是仅仅告诉编译器将y其视为整数等价物,而是明确告诉它以适合整数类的方式x进行更改,然后将结果分配给.xy

在您的特定情况下,强制转换执行所谓的“拆箱”,而Convert()实际上将获得整数值。结果看起来是一样的,但是Keith 更好地解释了细微的差异。

于 2008-09-27T17:12:28.010 回答
0

根据MCTS Self-Paced Training Kit (Exam 70-536): Microsoft® .NE​​T Framework 2.0-Application Development Foundation的第 1 章第 4 课第 55 页上标题为“显式转换的方法”的表 1-7 ,肯定有他们之间的区别。

System.Convert与语言无关,可以转换“在实现System.IConvertible 接口的类型之间”。

(type) cast operatorC# 特定的语言功能,可转换“定义转换运算符的类型之间”。

此外,在实施自定义转换时,它们之间的建议不同

根据上述课程中第 56-57 页标题为“如何在自定义类型中实现转换”的部分,转换运算符(强制转换)旨在简化数字类型之间的转换,而 Convert() 启用特定于文化的转换

您选择哪种技术取决于您要执行的转换类型:

  • 定义转换运算符以简化数值类型之间的缩小和扩大转换。

  • 实现System.IConvertible以通过 System.Convert 启用转换。使用此技术来启用特定于文化的转换。

  • ...

现在应该更清楚了,由于强制转换运算符是独立于 IConvertible 接口实现的,因此 Convert() 不一定只是强制转换的另一个名称。(但我可以设想一个实现可以在哪里引用另一个以确保一致性)。

于 2008-09-27T18:17:43.420 回答
-2

强制转换本质上只是告诉运行时“假装”对象是新类型。它实际上并没有以任何方式转换或更改对象。

但是,Convert 将执行将一种类型转换为另一种类型的操作。

举个例子:

char caster = '5';
Console.WriteLine((int)caster);

这些语句的输出将是 53,因为运行时所做的只是查看位模式并将其视为 int。你最终得到的是字符 5 的 ascii 值,而不是数字 5。

但是,如果您使用 Convert.ToInt32(caster),您将得到 5,因为它实际上会读取字符串并正确修改它。(基本上它知道 ASCII 值 53 实际上是整数值 5。)

于 2008-09-27T17:16:10.090 回答
-4

区别在于转换是隐式的还是显式的。上面的第一个是强制转换,第二个是对转换函数的更明确的调用。他们可能会以不同的方式做同样的事情。

于 2008-09-27T17:01:07.210 回答