铸造和胁迫有什么区别

本文关键字:什么 区别 | 更新日期: 2023-09-27 18:20:11

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

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

类型转换

(有时也称为类型转换(

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

非转换类型

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

不会更改基础位的更改。

强迫

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

铸造和胁迫有什么区别

类型转换:

单词转换是指隐式或显式地将值从一种数据类型更改为另一种数据类型,例如将 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

正如您所注意的,用法各不相同。

我的个人用法是:

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

  • "转换"是一种操作,通过该操作,一种类型的值被视为另一种类型的值 - 通常是不同的类型,尽管从技术上讲,"身份转换"仍然是转换。转换可能是"表示更改",例如 int 到双精度,或者可能是"表示保留",例如字符串到对象。转换可以是"隐式"(不需要强制转换(或"显式"(确实需要强制转换(。

  • "强制"是一种改变表示的隐式转换。

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

请注意,在前一个过程中不涉及转换,您有一个想要视为另一种类型的类型,例如,您有 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 复制数据来填充它。

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

根据维基百科,

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

类型强制转换

和类型强制的区别如下:

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

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

强制转换将保留对象的类型。 胁迫不会。

强制是获取与赋值不兼容的类型的值,并转换为与赋值兼容的类型。 在这里我执行胁迫,因为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() {}
    }

来源:The Common Language Infrastructure Annotated Standard,作者:James S. Miller

现在奇怪的是,Microsoft关于铸造的文档与 ecma-335 规范对铸造的定义不一致。

显式转换(强制转换

(:显式转换需要强制转换 算子。当信息可能在 转化,或者当其他转化可能不成功时 原因。典型示例包括数值转换为 精度较低或范围较小,并且转换了基类 实例到派生类。

。这听起来像是强制不投掷。

例如

  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.

谁知道呢? 也许Microsoft正在检查是否有人阅读这些东西。

从 CLI 标准:

I.8.3.2 胁迫

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

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

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

I.8.3.3 铸造

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

例如,当强制转换从以下位置读取的值时,可能需要进行运行时检查 类型化为保存特定接口的值。由于接口是一个不完整的描述 的值,将该值强制转换为不同的接口类型通常会导致运行时 演员检查。

以下是以下文章中的帖子:

胁迫和选角之间的区别往往被忽视。我明白为什么;对于这两种操作,许多语言具有相同(或相似(的语法和术语。有些语言甚至可能将任何转换称为"强制转换",但以下解释指的是 CTS 中的概念。

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

回想一下,值可以有多种类型。如果您的情况略有不同,并且您只想选择不同的值类型,则强制转换是该作业的工具。强制转换仅指示您希望对值包含的特定类型进行操作。

代码级别的差异因 C# 而异。在 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(( 和运算符 ==(( 来比较 Name 和字符串 [] 来使情况稍微好一点(或稍微更混乱一点(。这些运算符将"修复"比较问题,但您仍然有两个单独的实例;对字符串的任何修改都不会反映在名称或元组中,而对名称或元组的更改将反映在名称和元组中,但不会反映在字符串中。

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