强制转换和强制转换有什么区别?


85

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

也许有一种清晰,简单的方式来解释你们所知道的区别吗?

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

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

非转换类型强制转换(有时称为pun类型

不会更改基础位的更改。

强迫

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


Answers:


114

类型转换

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

胁迫用于表示的隐式转换。

通常是指一个明确的类型转换(相对于隐式转换),不管这是否是一个位模式或实转换的重新解释。

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


几个例子(来自同一来源):

强制(隐式):

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

这会使“内隐强制”变得多余吗?此处的注释同时使用了“隐式强制”和“显式强制”
Dave Cousineau

1
隐式转换只能在不损失精度或没有意义的情况下进行(例如:Int-> double)。在大多数现代语言中,您不能执行double-> int,因为这样会失去精度。使用类型强制,这不是一个“问题”。
Maxime Rouiller

该答案与ecma 335中为CIL定义的规范不符。我在答案中列出了带有示例的规范定义。
P.Brian.Mackey

24

您会注意到用法有所不同。

我的个人用法是:

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

  • 从技术上讲,“转换”是一种将一种类型的值视为另一种类型(通常是另一种类型)的值的操作,尽管“身份转换”仍然是一种转换。转换可能是“表示更改”,例如int到double,也可能是“表示保留”,例如字符串到对象。转换可以是“隐式”的,不需要强制转换,也可以是“显式”的,不需要强制转换。

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


1
我认为这个答案的第一句话是最重要的。不同的语言使用这些术语来表示完全不同的事物。例如,在Haskell中,“强制”永远不会改变表示形式。安全的强制性,Data.Coerce.coerce :: Coercible a b => a -> b适用于证明具有相同表示形式的类型;Unsafe.Coerce.unsafeCoerce :: a -> b适用于任何两种类型(如果使用不当,会使恶魔从您的鼻子中冒出来)。
dfeuer 2014年

@dfeuer有趣的数据点,谢谢!我注意到C#spec没有定义“强制”。我的建议正是我个人的意思。考虑到该术语定义不明确,我通常避免使用它。
埃里克·利珀特

8

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

请注意,在前一个过程中,不涉及任何转换,您有一个要当作其他类型的类型,例如,您有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隐式转换为基,这意味着不会创建新对象,因为obj2已经可以 baseClass
  • OBJ 3需要以某种方式转化为基础,你需要提供从您自己的方法转换otherClassbaseClass,这将涉及到创造型基类的一个新对象,并通过OBJ 3复制数据填充它。

一个很好的例子是Convert C#类,其中提供了可在不同类型之间进行转换的自定义代码。


3
一个示例将有助于阐明您要做出的区分。
奥利弗·查尔斯沃思

2

投射保留对象的类型。强制不这样做。

强制转换将使用与分配不兼容的类型的值,并将其转换为与分配兼容的类型。在这里,我执行强制是因为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的文档与ecma-335规范有关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.

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


1

以下是以下文章的帖子:

强制和强制转换之间的差异通常被忽略。我明白为什么了;许多语言的两种操作都具有相同(或相似)的语法和术语。某些语言甚至可能将任何转换称为“广播”,但以下说明涉及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

至于逻辑级别,有一些重要的区别。要记住的最重要的一点是,强制创造了新的价值,而铸造却没有。原始值的标识与强制转换后的值相同,而强制值的标识与原始值不同;强制会创建一个新的,独特的实例,而强制转换不会。结果是,强制转换的结果和原始结果将始终相等(在身份和相等性上),但是强制值可能等于或可能不等于原始值,并且永远不会共享原始身份。

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

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;

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

尽管上面的示例旨在说明强制转换和强制转换之间的一些差异,但它也很好地说明了为什么您应该非常谨慎地将转换运算符与C#中的引用类型一起使用。


1

CLI标准

I.8.3.2强制

有时,需要获取一个不可分配给位置的类型的值,并将该值转换为可分配给位置类型的类型的值。这是通过强制值来实现 的。强制采用特定类型和所需类型的值,并尝试创建与原始值具有相同含义的所需类型的值。强制会导致表示形式更改以及类型更改;因此,强制不一定保留对象的身份。

强制有两种:扩大(永远不会丢失信息)和缩小(可能丢失信息)。扩大强制的一个示例是将32位带符号整数的值强制为64位带符号整数的值。缩小强制的一个例子是相反的:将64位带符号整数强制为32位带符号整数。编程语言通常将强制转换实现为隐式转换,而强制转换通常需要显式转换

在内置类型的VES操作中直接内置了某些强制性(请参见§I.12.1)。所有其他强制措施均应明确要求。对于内置类型,CTS根据操作语义提供操作,以执行没有运行时检查的加宽强制,并通过运行时检查或截断来缩小强制。

I.8.3.3投放

由于值可以是一种以上的类型,因此使用该值需要清楚地标识正在使用的是哪种类型。由于从键入的位置读取值,因此使用的值的类型就是从中读取值的位置的类型。如果要使用其他类型,则将值强制转换为其其他类型之一。强制转换通常是编译时的操作,但是如果编译器不能静态地知道该值是目标类型,则执行运行时强制转换检查。与强制转换不同,强制转换永远不会更改对象的实际类型,也不会更改表示形式。投射保留对象的身份。

例如,当转换从键入为保存特定接口的值的位置读取的值时,可能需要运行时检查。由于接口是值的不完整描述,因此将该值强制转换为其他接口类型通常会导致运行时强制转换检查。


1

根据维基百科,

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

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

           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的话:

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.