Was ist der Unterschied zwischen Gießen und Zwingen?

83

Ich habe gesehen, dass beide Begriffe in verschiedenen Online-Erklärungen fast synonym verwendet werden, und die meisten Lehrbücher, die ich konsultiert habe, sind sich auch nicht ganz klar über die Unterscheidung.

Gibt es vielleicht eine klare und einfache Möglichkeit, den Unterschied zu erklären, den ihr kennt?

Typkonvertierung (manchmal auch als Typumwandlung bezeichnet )

So verwenden Sie einen Wert eines Typs in einem Kontext, der einen anderen erwartet.

Nicht konvertierender Typ Cast (manchmal auch als Typ Wortspiel bekannt )

Eine Änderung, die die zugrunde liegenden Bits nicht ändert.

Zwang

Prozess, bei dem ein Compiler automatisch einen Wert eines Typs in einen Wert eines anderen Typs konvertiert, wenn dieser zweite Typ vom umgebenden Kontext benötigt wird.

Alexandr Kurilin
quelle
4
Was ist mit dem Wikipedia-Artikel ?
Oliver Charlesworth

Antworten:

112

Typkonvertierung :

Die Wortkonvertierung bezieht sich entweder auf das implizite oder explizite Ändern eines Werts von einem Datentyp in einen anderen, z. B. eine 16-Bit-Ganzzahl in eine 32-Bit-Ganzzahl.

Das Wort Zwang wird verwendet, um eine implizite Umwandlung zu bezeichnen.

Das Wort Cast bezieht sich normalerweise auf eine explizite Typkonvertierung (im Gegensatz zu einer impliziten Konvertierung), unabhängig davon, ob dies eine Neuinterpretation eines Bitmusters oder eine echte Konvertierung ist.

Zwang ist also implizit, Besetzung ist explizit und Bekehrung ist eine davon.


Einige Beispiele (aus derselben Quelle ):

Zwang (implizit):

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

Besetzung (explizit):

double da = 3.3;
double db = 3.3;
double dc = 3.4;
int result = (int)da + (int)db + (int)dc; //result == 9
Igor Oks
quelle
würde dies "impliziten Zwang" überflüssig machen? Die Notiz hier verwendet sowohl "impliziten Zwang" als auch "expliziten Zwang"
Dave Cousineau
1
Eine implizite Konvertierung kann nur durchgeführt werden, wenn Sie nicht an Präzision verlieren oder Sinn machen (z. B.: Int -> double). In den meisten modernen Sprachen können Sie double-> int nicht ausführen, da Sie sonst an Präzision verlieren würden. Bei Typenzwang ist das kein "Problem".
Maxime Rouiller
Diese Antwort stimmt nicht mit den in ecma 335 für die CIL definierten Spezifikationen überein. Ich habe die Spezifikationsdefinition mit Beispielen in meiner Antwort dargelegt.
P.Brian.Mackey
23

Die Verwendung variiert, wie Sie bemerken.

Meine persönlichen Verwendungen sind:

  • Eine "Besetzung" ist die Verwendung eines Besetzungsoperators . Ein Cast-Operator weist den Compiler an, dass entweder (1) dieser Ausdruck nicht vom angegebenen Typ ist, aber ich verspreche Ihnen, dass der Wert zur Laufzeit von diesem Typ sein wird; Der Compiler behandelt den Ausdruck als vom angegebenen Typ, und die Laufzeit erzeugt einen Fehler, wenn dies nicht der Fall ist, oder (2) der Ausdruck ist vollständig vom anderen Typ, es gibt jedoch eine bekannte Möglichkeit, Instanzen zuzuordnen vom Typ des Ausdrucks mit Instanzen des Cast-to-Typs. Der Compiler wird angewiesen, Code zu generieren, der die Konvertierung durchführt. Der aufmerksame Leser wird feststellen, dass dies Gegensätze sind, was ich für einen ordentlichen Trick halte.

  • Eine "Konvertierung" ist eine Operation, bei der ein Wert eines Typs als Wert eines anderen Typs behandelt wird - normalerweise ein anderer Typ, obwohl eine "Identitätskonvertierung" technisch gesehen immer noch eine Konvertierung ist. Die Konvertierung kann "Repräsentationsänderung" sein, wie "int" zu "double", oder "Repräsentationserhaltung" wie "Zeichenfolge zu Objekt". Konvertierungen können "implizit" sein, für die keine Besetzung erforderlich ist, oder "explizit", für die eine Besetzung erforderlich ist.

  • Ein "Zwang" ist eine repräsentationsverändernde implizite Umwandlung.

Eric Lippert
quelle
1
Ich denke, der erste Satz dieser Antwort ist das Wichtigste von allen. Verschiedene Sprachen verwenden diese Begriffe, um ganz unterschiedliche Dinge zu bedeuten. In Haskell zum Beispiel ändert ein "Zwang" niemals die Darstellung; ein sicherer Zwang, Data.Coerce.coerce :: Coercible a b => a -> bfunktioniert für Typen, die nachweislich die gleiche Repräsentation haben; Unsafe.Coerce.unsafeCoerce :: a -> bfunktioniert für zwei beliebige Typen (und lässt Dämonen aus der Nase kommen, wenn Sie es falsch verwenden).
Feuer
@dfeuer interessanter Datenpunkt, danke! Ich stelle fest, dass die C # -Spezifikation nicht "Zwang" definiert; Mein Vorschlag ist genau das, was ich persönlich meine. Da der Begriff schlecht definiert zu sein scheint, vermeide ich ihn im Allgemeinen.
Eric Lippert
7

Casting ist der Prozess, bei dem Sie einen Objekttyp als einen anderen Typ behandeln. Coercing konvertiert ein Objekt in ein anderes.

Beachten Sie, dass im vorherigen Prozess keine Konvertierung erforderlich ist. Sie haben einen Typ, den Sie als einen anderen behandeln möchten. Angenommen, Sie haben 3 verschiedene Objekte, die von einem Basistyp erben, und Sie haben eine Methode, die dies übernimmt Basistyp: Wenn Sie jetzt den spezifischen untergeordneten Typ verwenden, können Sie ihn zu dem, was er ist, in CAST umwandeln und alle spezifischen Methoden und Eigenschaften dieses Objekts verwenden. Dadurch wird keine neue Instanz des Objekts erstellt.

Auf der anderen Seite impliziert das Erzwingen die Erstellung eines neuen Objekts im Speicher des neuen Typs, und dann wird der ursprüngliche Typ in den neuen kopiert, wobei beide Objekte im Speicher verbleiben (bis die Garbage Collectors eines oder beide entfernen). .

PedroC88
quelle
3
Ein Beispiel würde helfen, die Unterscheidung zu verdeutlichen, die Sie treffen möchten.
Oliver Charlesworth
2

Beim Casting bleibt die Art der Objekte erhalten. Zwang nicht.

Zwang nimmt den Wert eines Typs, der NICHT zuweisungskompatibel ist, und konvertiert ihn in einen Typ, der zuweisungskompatibel ist. Hier Int32mache ich einen Zwang, weil er NICHT von Int64... erbt, also ist er NICHT zuweisungskompatibel. Dies ist ein zunehmender Zwang (keine Daten verloren). Ein zunehmender Zwang ist eine implizite Bekehrung . Ein Zwang führt eine Konvertierung durch.

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

Mit Casting können Sie einen Typ so behandeln, als wäre er von einem anderen Typ, während Sie den Typ beibehalten .

    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() {}
    }

Quelle: The Common Language Infrastructure Annotated Standard von James S. Miller

Was jetzt seltsam ist, ist, dass die Microsoft-Dokumentation zu Casting nicht mit der ecma-335-Spezifikationsdefinition von Casting übereinstimmt.

Explizite Konvertierungen (Casts): Explizite Conversions erfordern einen Cast-Operator. Casting ist erforderlich, wenn Informationen bei der Konvertierung verloren gehen oder wenn die Konvertierung aus anderen Gründen möglicherweise nicht erfolgreich ist. Typische Beispiele sind die numerische Konvertierung in einen Typ mit geringerer Genauigkeit oder einem kleineren Bereich sowie die Konvertierung einer Basisklasseninstanz in eine abgeleitete Klasse.

... Das klingt nach Zwang ohne Casting.

Beispielsweise,

  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.

Wer weiß? Vielleicht prüft Microsoft, ob jemand dieses Zeug liest.

P.Brian.Mackey
quelle
1

Unten ist ein Beitrag aus dem folgenden Artikel :

Der Unterschied zwischen Zwang und Gießen wird oft vernachlässigt. Ich kann sehen warum; Viele Sprachen haben für beide Operationen dieselbe (oder eine ähnliche) Syntax und Terminologie. Einige Sprachen bezeichnen eine Konvertierung möglicherweise sogar als "Casting". Die folgende Erklärung bezieht sich jedoch auf Konzepte im CTS.

Wenn Sie versuchen, einem Speicherort eines anderen Typs einen Wert eines bestimmten Typs zuzuweisen, können Sie einen Wert des neuen Typs generieren, der eine ähnliche Bedeutung wie das Original hat. Das ist Zwang. Mit Zwang können Sie den neuen Typ verwenden, indem Sie einen neuen Wert erstellen, der in gewisser Weise dem Original ähnelt. Einige Zwänge verwerfen möglicherweise Daten (z. B. Konvertieren des int 0x12345678 in den kurzen 0x5678), andere möglicherweise nicht (z. B. Konvertieren des int 0x00000008 in den kurzen 0x0008 oder des langen 0x0000000000000008).

Denken Sie daran, dass Werte mehrere Typen haben können. Wenn Ihre Situation etwas anders ist und Sie nur einen anderen Werttyp auswählen möchten, ist das Casting das Werkzeug für den Job. Das Casting zeigt lediglich an, dass Sie einen bestimmten Typ bearbeiten möchten, den ein Wert enthält.

Der Unterschied auf Codeebene variiert von C # bis IL. In C # sehen Casting und Zwang ziemlich ähnlich aus:

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;
}

Auf IL-Ebene sind sie ganz anders:

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

In Bezug auf die logische Ebene gibt es einige wichtige Unterschiede. Was am wichtigsten ist, ist, dass Zwang einen neuen Wert schafft, Casting jedoch nicht. Die Identität des ursprünglichen Werts und der Wert nach dem Gießen sind gleich, während sich die Identität eines erzwungenen Werts vom ursprünglichen Wert unterscheidet. Durch das Zusammenwirken wird eine neue, eindeutige Instanz erstellt, durch das Casting jedoch nicht. Eine Konsequenz ist, dass das Ergebnis des Castings und des Originals immer gleichwertig sind (sowohl in Bezug auf Identität als auch in Bezug auf Gleichheit), aber ein erzwungener Wert kann gleich oder nicht gleich dem Original sein und niemals die ursprüngliche Identität teilen.

Die Auswirkungen von Zwang sind in den obigen Beispielen leicht zu erkennen, da die numerischen Typen immer nach Wert kopiert werden. Bei der Arbeit mit Referenztypen wird es etwas schwieriger.

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 };
    }
}

Im folgenden Beispiel ist eine Konvertierung eine Besetzung, während die andere ein Zwang ist.

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

Nach diesen Konvertierungen sind Tupel und Name gleich, aber Zeichenfolgen sind nicht gleich. Sie könnten die Situation etwas verbessern (oder etwas verwirrender), indem Sie Equals () und operator == () in der Name-Klasse implementieren, um einen Namen und eine Zeichenfolge [] zu vergleichen. Diese Operatoren würden das Vergleichsproblem "beheben", aber Sie hätten immer noch zwei separate Instanzen. Änderungen an Zeichenfolgen werden nicht in Namen oder Tupeln wiedergegeben, während Änderungen an Namen oder Tupeln in Namen und Tupel wiedergegeben werden, jedoch nicht in Zeichenfolgen.

Obwohl das obige Beispiel einige Unterschiede zwischen Gießen und Zwang veranschaulichen sollte, dient es auch als gutes Beispiel dafür, warum Sie bei der Verwendung von Konvertierungsoperatoren mit Referenztypen in C # äußerst vorsichtig sein sollten.

Gefangener NULL
quelle
1

Aus dem CLI-Standard :

I.8.3.2 Zwang

Manchmal ist es wünschenswert, einen Wert eines Typs zu verwenden, der keinem Standort zugewiesen werden kann, und den Wert in einen Typ zu konvertieren, der dem Typ des Standorts zugewiesen werden kann. Dies wird durch Zwang erreicht des Wertes erreicht. Zwang nimmt einen Wert eines bestimmten Typs und eines gewünschten Typs an und versucht, einen Wert des gewünschten Typs zu erstellen, der dem ursprünglichen Wert entspricht. Zwang kann sowohl zu einer Änderung der Darstellung als auch zu einer Änderung des Typs führen. Daher bewahrt Zwang nicht unbedingt die Objektidentität.

Es gibt zwei Arten von Zwang: Verbreiterung , bei der niemals Informationen verloren gehen, und Verengung , bei der Informationen verloren gehen können. Ein Beispiel für einen sich erweiternden Zwang wäre das Erzwingen eines Werts, der eine 32-Bit-Ganzzahl mit Vorzeichen ist, zu einem Wert, der eine 64-Bit-Ganzzahl mit Vorzeichen ist. Ein Beispiel für einen sich verengenden Zwang ist das Gegenteil: Erzwingen einer 64-Bit-Ganzzahl mit Vorzeichen in eine 32-Bit-Ganzzahl mit Vorzeichen. Programmiersprachen implementieren häufig Erweiterungszwänge als implizite Konvertierungen , während Verengungszwänge normalerweise eine explizite Konvertierung erfordern .

Einige Zwänge sind direkt in die VES-Operationen der integrierten Typen integriert (siehe §I.12.1). Jeder andere Zwang wird ausdrücklich verlangt. Für die integrierten Typen bietet das CTS Operationen zum Ausführen von Erweiterungszwängen ohne Laufzeitprüfungen und zum Eingrenzen von Zwängen mit Laufzeitprüfungen oder Abschneiden gemäß der Operationssemantik.

I.8.3.3 Casting

Da ein Wert von mehr als einem Typ sein kann, muss bei Verwendung des Werts klar angegeben werden, welcher seiner Typen verwendet wird. Da Werte von typisierten Orten gelesen werden, ist der Typ des verwendeten Werts der Typ des Ortes, von dem der Wert gelesen wurde. Wenn eine andere Art verwendet werden soll, wird der Wert gegossen in eine seiner anderen Typen. Das Casting ist normalerweise eine Kompilierungszeitoperation. Wenn der Compiler jedoch nicht statisch wissen kann, dass der Wert vom Zieltyp ist, wird eine Laufzeit-Casting-Prüfung durchgeführt. Im Gegensatz zu Zwang ändert eine Besetzung niemals den tatsächlichen Typ eines Objekts oder die Darstellung. Das Casting bewahrt die Identität von Objekten.

Beispielsweise kann eine Laufzeitprüfung erforderlich sein, wenn ein von einem Speicherort gelesener Wert umgewandelt wird, der als Wert einer bestimmten Schnittstelle angegeben ist. Da eine Schnittstelle eine unvollständige Beschreibung des Werts ist, führt das Umwandeln dieses Werts in einen anderen Schnittstellentyp normalerweise zu einer Laufzeitumwandlungsprüfung.

Naglas
quelle
1

Laut Wikipedia

In der Informatik sind Typkonvertierung, Typumwandlung, Typenzwang und Typjonglierung verschiedene Möglichkeiten, einen Ausdruck von einem Datentyp in einen anderen zu ändern.

Der Unterschied zwischen Typguss und Typzwang ist wie folgt:

           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.            |

Hinweis : Casting ist keine Konvertierung. Es ist nur der Prozess, mit dem wir einen Objekttyp als einen anderen Typ behandeln. Daher wird der tatsächliche Objekttyp sowie die Darstellung während des Gießens nicht geändert.

Ich stimme den Worten von @ PedroC88 zu:

Auf der anderen Seite impliziert das Erzwingen die Erstellung eines neuen Objekts im Speicher des neuen Typs, und dann wird der ursprüngliche Typ in den neuen kopiert, wobei beide Objekte im Speicher verbleiben (bis die Garbage Collectors eines oder beide entfernen). .

Palak Jain
quelle