铸造和转换有什么区别?

时间:2010-07-02 14:59:02

标签: c# type-conversion casting

Eric Lippert在this question中的评论让我彻底感到困惑。 C#中的转换和转换有什么区别?

13 个答案:

答案 0 :(得分:80)

Casting是一种告诉编译器的方法“对象X实际上是Y型,继续进行处理。”

转换说“我知道对象X不是Y型,但是存在一种从Y型X创建新对象的方法,继续执行它。”

答案 1 :(得分:45)

我相信Eric想要说的是:

投射是一个描述语法的术语(因此语法含义)。

转换是一个术语,用于描述幕后实际采取的操作(以及语义含义)。

  

使用cast-expression进行转换   明确表达给定的   类型。

  

形式(T)E的演员表达,   其中T是一个类型,E是一个   unary-expression,执行显式   E的值的转换(§13.2)   输入T。

似乎通过说语法中的强制转换运算符执行显式转换来支持它。

答案 2 :(得分:42)

我想起理查德·费曼所讲的轶事,他正在参加一个哲学课,教授问他“费曼,你是一个物理学家,在你看来,电子是一个'必不可少的对象'?”所以费曼问澄清问题“砖块是一个必不可少的东西吗?”上课。每个学生对这个问题都有不同的答案。他们说,“砖”的基本抽象概念是必不可少的对象。不,一个特定的,独特的砖是必不可少的对象。不,你可以凭经验观察到的砖块部分是必不可少的。等等。

当然不回答你的问题。

我不会经历所有这十几个答案,并与作者讨论我的真正含义。几个星期后我会写一篇关于这个主题的博客文章,我们会看看是否会对这个问题有所了解。

虽然是一个类比,但是费曼。你想在周六早上烤一条香蕉面包(就像我几乎每个星期六早上一样。)所以你咨询烹饪的喜悦,它说“等等等等......在另一个碗里,把干的成分搅拌在一起。” ..“

显然,明天早上该指令与您的行为之间存在着密切的关系,但同样明显的是将指令行动混淆是错误的。该指令由文本组成。它在特定页面上有一个位置。它有标点符号。如果你在厨房里一起搅拌面粉和小苏打,有人问“你现在的标点是什么?”,你可能认为这是一个奇怪的问题。该操作与指令有关,但指令的文本属性不是操作的属性。

演员不是转换,就像配方不是烘烤蛋糕的行为一样。配方是描述操作的文本,然后您可以执行该操作。转换操作符是描述操作的文本 - 转换 - 然后运行时可以执行。

答案 3 :(得分:7)

来自C#Spec 14.6.6:

  

使用cast-expression进行转换   明确表达给定的   类型。
  ...
  形式为(T)E的演员表达,   其中T是一个类型,E是一个   unary-expression,执行显式   E的值的转换(§13.2)   输入T。

因此,cast是一个语法结构,用于指示编译器调用显式转换。

来自C#Spec§13:

  

转换可以表达   一种类型被视为另一种类型   类型。转化可以是隐含的或   显式,这决定了是否   需要明确的演员表。   [例如:转换   从类型int到类型long是   隐式,所以int类型的表达式   可以隐含地被视为类型   长。相反的转换,来自   输入long类型为int,是显式的,所以   需要明确的演员表。

因此,转换是实际工作完成的地方。您会注意到,cast-expression引用它表示它执行显式转换,但显式转换是隐式转换的超集,因此您也可以通过强制转换表达式调用隐式转换(即使您不需要)。

答案 4 :(得分:6)

只是我的理解,可能太简单了:

当投射基本数据时保持不变(相同的内部表示) - “我知道这是一个字典,但你可以将它用作ICollection”。

转换时,您将内部表示更改为其他内容 - “我希望此int为字符串”。

答案 5 :(得分:5)

读完埃里克的评论之后,用简单的英语尝试:

投射表示这两种类型在某种程度上实际上是相同的。它们可以实现相同的接口或从相同的基类继承,或者目标可以“相同”(超集?)使转换工作,例如从Int16转换为Int32。

转换类型则意味着这两个对象可能足够相似以进行转换。以数字的字符串表示为例。它是一个字符串,它不能简单地转换为数字,需要进行解析并从一个转换为另一个,并且该过程可能会失败。它也可能因铸造而失败,但我认为这是一个更便宜的失败。

这就是我认为的两个概念之间的关键区别。转换需要对源数据进行某种解析或更深入的分析和转换。转换不解析。它只是在一些多态级别尝试匹配。

答案 6 :(得分:2)

投射是从另一种类型的另一个值创建一种类型的值。 转换是一种投射类型,其中值的内部表示也必须更改(而不仅仅是其解释)。

在C#中,使用强制转换表达式完成转换和转换:

type unary-expression

区别很重要(并且在评论中提出了这一点),因为只有转化可以由 conversion-operator-declarator 创建。因此,只能在代码中创建(隐式或显式)转换。

非转换隐式转换始终可用于子类型到超类型转换,并且非转换显式转换始终可用于超类型转换为子类型转换。不允许其他非转换演员表。

答案 7 :(得分:1)

在此上下文中,强制转换意味着您将某个给定类型的对象暴露为其他类型的操作,转换意味着您实际上将给定类型的对象更改为另一个类型的对象。

答案 8 :(得分:0)

MSDN C#文档的

This page表明强制转换是特定的转换实例:“显式转换”。也就是说,x = (int)y形式的转换是强制转换。

自动数据类型更改(例如myLong = myInt)是更通用的“转换”。

答案 9 :(得分:0)

强制转换是类/结构上的运算符。转换是受影响的类/结构中的一个或另一个上的方法/过程,或者可以是完全不同的类/结构(即Converter.ToInt32()

Cast操作符有两种形式:隐式和显式

隐式转换运算符表示一种类型的数据(例如,Int32)可以总是表示为另一种类型(十进制)而不会丢失数据/精度

int i = 25;
decimal d = i;

显式强制转换运算符表示一种类型(十进制)的数据始终忠实地表示为另一种类型(int),但可能会丢失数据/精度。因此,编译器要求您显式表明您已经意识到这一点并且想要通过使用显式强制转换语法来实现它:

decimal d = 25.0001;
int i = (int)d;

转换采用两种不一定相关的类型,并尝试通过某种过程将一种转换为另一种,例如解析。如果所有已知的转换算法都失败,则该过程可能会抛出异常或返回默认值:

string s = "200";
int i = Converter.ToInt32(s); // set i to 200 by parsing s

string s = "two hundred";
int i = Converter.ToInt32(s); // sets i to 0 because the parse fails

Eric对语法转换与语义转换的引用基本上是运算符与方法的区别。

答案 10 :(得分:0)

演员阵容是句法的,可能涉及也可能不涉及转换(取决于演员的类型)。如您所知,C ++允许指定您要使用的the type of cast

根据您询问的人(以及他们正在谈论的语言!)

,可能会或可能不会将层次结构向上/向下转换为转换。

Eric(C#)说,转换为其他类型总是涉及转换,但转换甚至可能不会更改实例的内部表示。

一个C ++ - 家伙会不同意,因为static_cast可能不会产生任何额外的代码(所以“转换”实际上并不真实!)

答案 11 :(得分:0)

在C#中,转换和转换基本上是相同的概念,除了可以使用Object.ToString()之类的任何方法进行转换。仅使用其他帖子中描述的强制转换操作符(T) E进行强制转换,并且可以使用转换或装箱。

它使用哪种转换方法?编译器根据编译时提供给编译器的类和库来决定。如果存在隐式转换,则不需要使用转换运算符。 Object o = String.Empty。如果仅存在显式转换,则必须使用转换运算符。 String s = (String) o

您可以在自己的班级中使用create explicitimplicit转换运算符。注意:转换可以使数据与您和我看起来非常相似或类似于原始类型,但它们都是由转换方法定义的,并使其对编译器合法。

施放始终是指使用施法操作符。你可以写

Object o = float.NaN;
String s = (String) o;

但是,如果您访问s,例如Console.WriteLine,则会收到运行时InvalidCastException。因此,强制转换操作符仍然会尝试在访问时使用转换,但在分配期间将解决装箱问题。

答案 12 :(得分:-6)

INSERTED EDIT#2:由于我提供了这个答案,所以这个问题已被标记为重复了一个问题的问题," 是同样的事情。转换?&#34 ;. " No"的答案被绝大多数人赞成。然而,我在这里的答案指出了为什么演员阵容与转换不同的生成本质被压倒性地低估了( 但我在评论中有一个+1 )。我认为读者很难理解演员表适用于指称语法/语义层,转换适用于操作语义层。例如,引用(或C / C ++中的指针) - 引用盒装数据类型 - 转换为另一种数据类型,不会(在所有语言和场景中)生成盒装数据的转换。例如,在C float a[1]; int* p = (int*)&a;中,我们并未确保*p引用int数据。

编译器从denotational语义编译为operational semantics。编译不是双射的,即它不能保证解编译(例如Java,LLVM,asm.js或C#字节码)回到编译到该字节码的任何指称语法(例如Scala,Python,C#,C via Emscripten等)。因此两层不一样。

因此,最明显的是' 演员'和' 转换'不是一回事。我的回答是指出这些术语适用于两个不同的语义层。强制转换适用于指示层(编译器的输入语法)所知道​​的语义。转换适用于操作(运行时或中间字节码)层知道的语义。我使用了标准术语“删除”#39;描述在操作语义层中没有明确记录的指称语义会发生什么。

例如,具体化的泛型是在操作语义层中记录指称语义的一个例子,但它们的缺点是使操作语义层与高阶指称语义不相容,例如,这就是为什么考虑在C#的CLR上实现Scala的高级泛型,因为C#的泛型的指称语义在操作语义层被硬编码。

来吧,伙伴们,停止低估那些比你知道更多的人。在投票之前先做好作业。


INSERTED EDIT:Casting是一个在指称语义层发生的操作(其中的类型以其完整语义表示)。演员表可以(例如显式转换)或不可以(例如,向上转换)在运行时语义层处引起转换。对我的回答(以及对Marc Gavin的评论的支持)的暗示表明,大多数人都不理解denotational semantics and operational (execution) semantics之间的差异。叹息。


对于所有语言,我会更简单,更一般地说明Eric Lippert的答案,包括C#

cast 是语法,所以(就像所有语法一样)在编译时擦除;然而,转换会在运行时导致一些操作


这是我在整个宇宙中所知道的每种计算机语言的真实陈述。请注意,上述声明并未说明投射转化是互斥的。

强制转换可能会在运行时导致转换,但有时可能不会。

我们有两个不同的词,即 cast conversion ,我们需要一种方法来分别描述语法中发生的事情(演员操作员)和运行时(转换,或类型检查和可能的转换)。

我们必须保持这种概念分离,因为在某些编程语言中,强制转换不会导致转换。另外,我们理解隐式转换(例如upcasting)仅在编译时发生。我写这个答案的原因是因为我想帮助读者理解多语言与计算机语言。此外,还要了解该通用定义如何在C#案例中正确应用。

此外,我想帮助读者了解我如何在脑海中概括概念,这有助于我成为计算机语言设计师。我试图传递一种非常简化,抽象的思维方式的礼物。但我也试图以非常实际的方式解释这一点。如果我需要改进说明,请随时在评论中告诉我。


Eric Lippert写道:

  

转换不是转换,就像配方不是转换一样   烤蛋糕的行为。食谱是描述动作的文字,   然后你可以执行。演员操作符是描述文本的文本   action - 转换 - 运行时可以执行转换。

配方是语法中发生的事情。语法总是被擦除,并且替换为无或运算符代码。

例如,我可以在C#中编写一个什么都不做的转换,并且在编译时被does not cause a change in the storage requirementsupcasting完全删除。我们可以清楚地看到,强制转换只是语法,不会对运行时代码进行任何更改。

int x = 1;
int y = (int)x;
Giraffe g = new Giraffe();
Animal a = (Animal)g;

这可以用于文档目的(但是很嘈杂),但它在具有类型推断的语言中是必不可少的,其中有时需要使用强制转换来告诉编译器您希望它推断出哪种类型。

对于an example,在Scala中,None的类型为Option[Nothing],其中Nothing sub 类型的底部类型所有可能的类型(不是 super -type)。所以有时当使用None时,需要将类型转换为特定类型,因为Scala only does local type inference因此无法推断出您想要的类型。

// (None : Option[Int]) casts None to Option[Int]
println(Some(7) <*> ((None : Option[Int]) <*> (Some(9) > add)))

演员可以在编译时知道它需要进行类型转换,例如int x = (int)1.5,或者可能需要在运行时进行类型检查和可能的类型转换,例如downcasting。演员表(即语法)将被删除并替换为运行时操作。

因此,我们可以清楚地看到,将所有演员表等同于显式转换,是MSDN documentation中的含义错误。该文档打算表明显式转换需要强制转换运算符,但它不应该试图暗示所有强制转换都是显式转换。我相信Eric Lippert在撰写他答复中承诺的博客时可以清除这一点。


ADD :在评论和聊天中,我可以看到对已删除一词的含义存在一些疑惑。

术语“删除”&#39;用于描述在编译时已知的信息,这在运行时是未知的。例如,类型可以在非实体化的泛型中删除,并且称为type erasure

一般来说,所有语法都被删除,因为通常CLI不是双向的(可逆的,并且一对一)与C#。您不能总是从一些任意CLI代码返回到确切的C#源代码。这意味着信息已被删除。

那些说擦除的人不是正确的术语,正在将演员的实现与演员的语义混为一谈。演员是一个更高级别的语义(我认为它实际上高于语法,它至少在上传和下传的情况下是指称语义)在我们想要转换的那个语义级别上说类型。现在如何在运行时完成这是完全不同的语义级别。在某些语言中,它可能始终是NOOP。例如,在Haskell中,所有输入信息都在编译时被删除。