强制转换和强制转换有啥区别?

Posted

技术标签:

【中文标题】强制转换和强制转换有啥区别?【英文标题】:What is the difference between casting and coercing?强制转换和强制转换有什么区别? 【发布时间】:2012-02-10 01:56:39 【问题描述】:

我已经看到这两个术语在各种在线解释中几乎可以互换使用,而且我查阅过的大多数教科书也并不完全清楚区别。

也许有一种清晰而简单的方法来解释你们所知道的差异?

类型转换(有时也称为类型转换

在需要另一种类型的上下文中使用一种类型的值。

非转换类型转换(有时称为类型双关语

不会改变底层位的更改。

强制

当周围上下文需要第二种类型时,编译器自动将一种类型的值转换为另一种类型的值的过程。

【问题讨论】:

Wikipedia article 怎么样? 【参考方案1】:

Type Conversion:

conversion 一词是指隐式或显式地将值从一种数据类型更改为另一种数据类型,例如一个 16 位整数到一个 32 位整数。

coercion这个词用来表示隐式转换。

cast这个词通常指的是显式类型转换(与隐式转换相反),无论这是对位模式的重新解释还是真正的转换。 p>

因此,强制是隐式的,强制转换是显式的,转换是其中的任何一个。


几个例子(来自same source):

强制(隐式):

double  d;
int     i;
if (d > i)      d = i;

演员表(显式):

double da = 3.3;
double db = 3.3;
double dc = 3.4;
int result = (int)da + (int)db + (int)dc; //result == 9

【讨论】:

这会使“隐式强制”变得多余吗?注释here 同时使用“隐式强制”和“显式强制” 只有在不丢失精度或没有意义的情况下才能进行隐式转换(例如:Int -> double)。在大多数现代语言中,你不能做 double-> int 因为你会失去精度。使用类型强制,这不是“问题”。 此答案与 ecma 335 中为 CIL 定义的规范不一致。我在回答中列出了规范定义和示例。【参考方案2】:

如您所见,用法各不相同。

我个人的用法是:

“强制转换”是强制转换操作符的用法。强制转换运算符指示编译器 (1) 该表达式不知道属于给定类型,但我向您保证该值在运行时将属于该类型;编译器会将表达式视为给定类型,如果不是,则运行时将产生错误,或者 (2) 表达式完全属于不同的类型,但是有一种众所周知的方法来关联实例具有强制转换类型的实例的表达式类型。编译器被指示生成执行转换的代码。细心的读者会注意到这些是相反的,我认为这是一个巧妙的技巧。

“转换”是一种操作,其中一种类型的值被视为另一种类型的值——通常是不同的类型,尽管从技术上讲,“身份转换”仍然是一种转换。转换可能是“表示变化”,如 int 到 double,或者它可能是“表示保留”,如字符串到对象。转换可以是“隐式”的,不需要强制转换,也可以是“显式”的,需要强制转换。

“强制”是一种改变表示的隐式转换。

【讨论】:

我认为这个答案的第一句话是最重要的。不同的语言使用这些术语来表示完全不同的事物。例如,在 Haskell 中,“强制”从不改变表示;安全强制,Data.Coerce.coerce :: Coercible a b => a -> b 适用于被证明具有相同表示的类型; Unsafe.Coerce.unsafeCoerce :: a -> b 适用于任何两种类型(如果你用错了,会让人从你的鼻子里出来)。 @dfeuer 有趣的数据点,谢谢!我注意到 C#spec 没有定义“强制”;我的建议就是我个人的意思。鉴于该术语似乎定义不明确,我通常会避免使用它。【参考方案3】:

强制转换是将对象类型视为另一种类型的过程,强制转换是将一个对象转换为另一个对象。

请注意,在前一个过程中不涉及转换,您有一个类型,您希望将其视为另一个类型,例如,您有 3 个不同的对象继承自一个基类型,并且您有一个方法将在任何时候采用该基本类型,如果您知道特定的子类型,则可以将其转换为它的本质并使用该对象的所有特定方法和属性,并且不会创建该对象的新实例。

另一方面,强制意味着在新类型的内存中创建一个新对象,然后将原始类型复制到新类型,将两个对象都留在内存中(直到垃圾收集器带走任何一个,或两者兼有)。

以下面的代码为例:

class baseClass 
class childClass : baseClass 
class otherClass 

public void doSomethingWithBase(baseClass item) 

public void mainMethod()

    var obj1 = new baseClass();
    var obj2 = new childClass();
    var obj3 = new otherClass();

    doSomethingWithBase(obj1); //not a problem, obj1 is already of type baseClass
    doSomethingWithBase(obj2); //not a problem, obj2 is implicitly casted to baseClass
    doSomethingWithBase(obj3); //won't compile without additional code

obj1 在没有任何强制转换或强制(转换)的情况下传递,因为它已经属于同一类型 baseClass obj2 被隐式转换为 base,这意味着不会创建新对象,因为 obj2 已经可以是 baseClass obj3 需要以某种方式转换为 base,您需要提供自己的方法将 otherClass 转换为 baseClass,这将涉及创建一个 baseClass 类型的新对象并通过从obj3。

Convert C# class 就是一个很好的例子,它提供自定义代码以在不同类型之间进行转换。

【讨论】:

一个例子将有助于阐明您试图做出的区分。【参考方案4】:

铸造保留了对象的类型。强制没有。

Coercion 正在获取与赋值不兼容的类型的值并将其转换为与赋值兼容的类型。在这里我执行强制,因为Int32 不继承自Int64...所以它不兼容分配。这是一个扩大的强制(没有数据丢失)。扩大强制转换也称为隐式转换强制执行转换。

void Main()

    System.Int32 a = 100;
    System.Int64 b = a;
    b.GetType();//The type is System.Int64.  

强制转换允许您将类型视为不同类型,同时还保留类型

    void Main()
    
        Derived d = new Derived();
        Base bb = d;
        //b.N();//INVALID.  Calls to the type Derived are not possible because bb is of type Base
        bb.GetType();//The type is Derived.  bb is still of type Derived despite not being able to call members of Test
    

    class Base 
    
        public void M() 
    

    class Derived: Base
    
        public void N() 
    

来源:James S. Miller 的公共语言基础结构注释标准

现在奇怪的是,Microsoft 在Casting 上的文档与 Casting 的 ecma-335 规范定义不一致。

显式转换(强制转换):显式转换需要强制转换 操作员。当信息可能在 转换,或者当其他转换可能不成功时 原因。典型示例包括将数值转换为 具有较低的精度或较小的范围,以及基类的转换 派生类的实例。

...这听起来像 Coercions 而不是 Casting。

例如,

  object o = 1;
  int i = (int)o;//Explicit conversions require a cast operator
  i.GetType();//The type has been explicitly converted to System.Int32.  Object type is not preserved.  This meets the definition of Coercion not casting.

谁知道?也许微软正在检查是否有人读过这些东西。

【讨论】:

【参考方案5】:

以下是来自following article的帖子:

强制转换和强制转换之间的区别经常被忽略。我明白为什么;许多语言对这两种操作都有相同(或相似)的语法和术语。某些语言甚至可能将任何转换称为“强制转换”,但以下解释指的是 CTS 中的概念。

如果您尝试将某种类型的值分配给不同类型的位置,则可以生成与原始值具有相似含义的新类型值。这是胁迫。 Coercion 允许您通过创建在某种程度上类似于原始值的新值来使用新类型。一些强制可能会丢弃数据(例如,将 int 0x12345678 转换为短 0x5678),而其他可能不会(例如,将 int 0x00000008 转换为短 0x0008,或长 0x0000000000000008)。

回想一下,值可以有多种类型。如果您的情况略有不同,并且您只想选择一种不同的值类型,那么强制转换就是这项工作的工具。强制转换只是表示​​您希望对值包含的特定类型进行操作。

代码级别的差异从 C# 到 IL。在 C# 中,强制转换和强制转换看起来都非常相似:

static void ChangeTypes(int number, System.IO.Stream stream)

    long longNumber = number;
    short shortNumber = (short)number;

    IDisposable disposableStream = stream;
    System.IO.FileStream fileStream = (System.IO.FileStream)stream;

在 IL 级别上,它们完全不同:

ldarg.0
 conv.i8
 stloc.0

ldarg.0
 conv.i2
 stloc.1


ldarg.1
 stloc.2

ldarg.1
 castclass [mscorlib]System.IO.FileStream
 stloc.3

至于逻辑层面,有一些重要的区别。最重要的是要记住,强制创造了新的价值,而强制转换没有。原值的身份与强制转换后的值相同,而强制值的身份与原值不同; coersion 创建一个新的、不同的实例,而强制转换不会。一个推论是强制转换的结果和原来的结果总是等价的(在同一性和相等性上),但是一个强制值可能等于也可能不等于原来的值,并且永远不会共享原来的同一性。

在上面的示例中很容易看出强制的含义,因为数字类型总是按值复制。当您使用引用类型时,事情会变得有些棘手。

class Name : Tuple<string, string>

    public Name(string first, string last)
        : base(first, last)
    
    

    public static implicit operator string[](Name name)
    
        return new string[]  name.Item1, name.Item2 ;
    

在下面的示例中,一种转换是强制转换,而另一种是强制转换。

Tuple<string, string> tuple = name;
string[] strings = name;

经过这些转换后,tuple 和 name 相等,但 strings 不相等。您可以通过在 Name 类上实现 Equals() 和 operator ==() 来比较 Name 和 string[],从而使情况稍微好一些(或者更容易混淆)。这些运算符将“修复”比较问题,但您仍然会有两个单独的实例;对字符串的任何修改都不会反映在名称或元组中,而对名称或元组之一的更改将反映在名称和元组中,但不会反映在字符串中。

虽然上面的示例旨在说明强制转换和强制转换之间的一些区别,但它也可以作为一个很好的示例,说明为什么在 C# 中使用带有引用类型的转换运算符时应格外小心。

【讨论】:

【参考方案6】:

来自the CLI standard:

I.8.3.2 强制

有时需要取一个不可分配给位置的类型的值,然后转换 可分配给位置类型的类型的值。这是通过 强制值。强制采用特定类型和所需类型的值并尝试 创建与原始值具有等效含义的所需类型的值。强迫 可能导致表示更改以及类型更改;因此强制不一定 保留对象身份。

有两种强制:加宽,永远不会丢失信息,缩小,在 哪些信息可能会丢失。一个扩大强制的例子是强制一个值 这是一个 32 位有符号整数到一个 64 位有符号整数的值。一个例子 缩小强制是相反的:将 64 位有符号整数强制为 32 位有符号整数。 编程语言通常将扩展强制转换为隐式转换,而 缩小强制转换通常需要显式转换

一些强制直接内置到内置类型的 VES 操作中(参见 §I.12.1)。全部 应明确要求其他强制。对于内置类型,CTS 提供操作 在没有运行时检查的情况下执行扩大强制并在运行时缩小强制 根据操作语义检查或截断。

I.8.3.3 铸造

由于一个值​​可以是多个类型,因此该值的使用需要清楚地识别出哪一个 它的类型正在被使用。由于值是从键入的位置读取的,因此值的类型 使用的是从中读取值的位置的类型。如果要使用不同的类型 被使用时,该值被 cast 转换为它的其他类型之一。强制转换通常是编译时操作, 但是如果编译器不能静态地知道该值是目标类型,则运行时强制转换检查 已经完成了。与强制不同,强制转换永远不会改变对象的实际类型,也不会改变对象的类型。 表示。强制转换保留了对象的身份。

例如,在转换从某个位置读取的值时,可能需要运行时检查 类型化为持有特定接口的值。由于接口是不完整的描述 的值,将该值转换为不同的接口类型通常会导致运行时 强制检查。

【讨论】:

【参考方案7】:

根据***,

在计算机科学中,类型转换、类型转换、类型强制和类型杂耍是将表达式从一种数据类型更改为另一种数据类型的不同方法。

类型转换和类型强制的区别如下:

           TYPE CASTING           |                   TYPE COERCION
                                  |
1. Explicit i.e., done by user    | 1. Implicit i.e., done by the compiler
                                  |
2. Types:                         | 2. Type:
    Static (done at compile time) |     Widening (conversion to higher data 
                                  |     type)
    Dynamic (done at run time)    |     Narrowing (conversion to lower data 
                                  |     type)
                                  |
3. Casting never changes the      | 3. Coercion can result in representation 
   the actual type of object      |    as well as type change.
   nor representation.            |

注意:强制转换不是转换。这只是我们将对象类型视为另一种类型的过程。因此,对象的实际类型以及表示在铸造期间不会改变。

我同意@PedroC88 的话:

另一方面,强制意味着在 新类型的内存,然后原始类型将被复制 到新的,将两个对象都留在内存中(直到垃圾 收藏家要么带走,要么两者都带走)。

【讨论】:

以上是关于强制转换和强制转换有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章

C ++中这两种强制转换方法有啥区别? [复制]

SQL SERVER中强制类型转换cast和convert的区别

SQL SERVER中强制类型转换cast和convert的区别

SQL SERVER中强制类型转换cast和convert的区别

SQL SERVER中强制类型转换cast和convert的区别

JS强制类型转换,隐式类型转换, == 和===的区别