キャスティングと強制の違いは何ですか?


85

両方の用語がさまざまなオンライン説明でほぼ同じ意味で使用されているのを見てきました。また、私が調べたほとんどの教科書でも、その違いについて完全には明確ではありません。

あなたたちが知っている違いを説明する明確で簡単な方法はおそらくありますか?

型変換型キャストとも呼ばれます)

別のタイプを予期するコンテキストで1つのタイプの値を使用すること。

Nonconverting型キャスト(時にはとして知られているタイプのしゃれ

基になるビットを変更しない変更。

強制

周囲のコンテキストで2番目の型が必要な場合に、コンパイラが1つの型の値を別の型の値に自動的に変換するプロセス。


回答:


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 2015

1
暗黙的な変換は、精度が失われていないか、意味がない場合にのみ実行できます(例:Int-> double)。ほとんどの現代語では、精度が失われるため、double-> intを実行することはできません。型強制では、それは「問題」ではありません。
Maxime Rouiller 2016

この回答は、CILのecma335で定義されている仕様と一致していません。私は答えの例を使って仕様の定義をレイアウトしました。
P.Brian.Mackey 2018

24

ご存知のように、使用法はさまざまです。

私の個人的な使用法は次のとおりです。

  • 「キャスト」とは、キャスト演算子の使用法です。キャスト演算子はコンパイラに次のいずれかを指示します。(1)この式が指定されたタイプであることがわからないが、実行時に値がそのタイプになることを約束します。コンパイラは式を指定されたタイプとして処理し、そうでない場合はランタイムがエラーを生成するか、(2)式が完全に異なるタイプであるが、インスタンスを関連付けるよく知られた方法があります式の型と​​キャスト先型のインスタンスの組み合わせ。コンパイラーは、変換を実行するコードを生成するように指示されます。注意深い読者は、これらが反対であることに気付くでしょう、それは私が巧妙なトリックだと思います。

  • 「変換」とは、あるタイプの値を別のタイプの値として扱う操作です。技術的に言えば、「ID変換」は依然として変換ですが、通常は別のタイプです。変換は、intからdoubleへのように「表現の変更」である場合もあれば、文字列からオブジェクトへのように「表現の保持」である場合もあります。変換は、キャストを必要としない「暗黙的」またはキャストを必要とする「明示的」の場合があります。

  • 「強制」とは、表現を変える暗黙の変換です。


1
この答えの最初の文は、すべての中で最も重要なことだと思います。言語が異なれば、これらの用語はまったく異なる意味で使用されます。たとえば、Haskellでは、「強制」によって表現が変わることはありません。安全な強制、Data.Coerce.coerce :: Coercible a b => a -> b同じ表現を持つことが証明されたタイプに対して機能します。Unsafe.Coerce.unsafeCoerce :: a -> b任意の2つのタイプで機能します(間違って使用すると、悪魔が鼻から出てきます)。
dfeuer 2014年

@dfeuer興味深いデータポイント、ありがとう!C#specは「強制」を定義していないことに注意してください。私の提案は私が個人的に意味するものです。用語が十分に定義されていないように思われることを考えると、私は一般的にそれを避けます。
Eric Lippert 2014年

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
  • OBJ3ニーズをベースに何らかの形で変換するには、次のように変換する独自の方法を提供する必要がありますotherClassへのbaseClass型基底クラスの新しいオブジェクトを作成し、OBJ3からデータをコピーして、それを埋める伴うであろう。

良い例は、異なるタイプ間で変換するためのカスタムコードを提供するConvert C#クラスです。


3
例は、あなたが作ろうとしている区別を明確にするのに役立ちます。
オリバーチャールズワース2012年

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によるCommonLanguage Infrastructure Annotated Standard

奇妙なことに、Castingに関するMicrosoftのドキュメントは、Castingの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.

知るか?たぶんマイクロソフトは誰かがこのようなものを読んでいるかどうかをチェックしています。


1

以下は、次の記事からの投稿です。

強制とキャスティングの違いはしばしば無視されます。理由がわかります。多くの言語では、両方の操作で同じ(または類似の)構文と用語が使用されています。一部の言語では、変換を「キャスト」と呼ぶこともありますが、以下の説明では、CTSの概念について説明します。

あるタイプの値を別のタイプの場所に割り当てようとしている場合は、元のタイプと同様の意味を持つ新しいタイプの値を生成できます。これは強制です。Coercionを使用すると、元の値に何らかの形で似た新しい値を作成することで、新しいタイプを使用できます。一部の強制はデータを破棄する場合があり(たとえば、int 0x12345678を短い0x5678に変換する)、他の強制は破棄しない場合があります(たとえば、int 0x00000008を短い0x0008または長い0x0000000000000008に変換する)。

値には複数のタイプがある可能性があることを思い出してください。状況が少し異なり、値のタイプの1つだけを選択したい場合は、キャストがその仕事のツールです。キャストは、値に含まれる特定のタイプを操作することを単に示します。

コードレベルでの違いは、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

論理レベルに関しては、いくつかの重要な違いがあります。覚えておくべき最も重要なことは、強制は新しい価値を生み出すが、キャストはそうではないということです。元の値のIDとキャスト後の値は同じですが、強制された値のIDは元の値とは異なります。強制は新しい別個のインスタンスを作成しますが、キャストは作成しません。当然の結果として、キャストの結果とオリジナルは常に同等になります(同一性と同等性の両方)が、強制された値はオリジナルと等しい場合と等しくない場合があり、元のアイデンティティを共有することはありません。

数値型は常に値によってコピーされるため、上記の例では強制の影響を簡単に確認できます。参照型を操作しているときは、少し注意が必要です。

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とstring []を比較することで、状況を少し良く(または少し混乱させて)す​​ることができます。これらの演算子は比較の問題を「修正」しますが、それでも2つの別々のインスタンスがあります。文字列への変更は名前またはタプルに反映されませんが、名前またはタプルのいずれかへの変更は名前とタプルに反映されますが、文字列には反映されません。

上記の例は、キャストと強制の違いを説明することを目的としていますが、C#で参照型を使用して変換演算子を使用する場合に非常に注意する必要がある理由の優れた例としても役立ちます。


1

CLI標準から:

I.8.3.2強制

場所に割り当てられないタイプの値を取得し、その値を場所のタイプに割り当て可能なタイプに変換することが望ましい場合があります。これは強制によって達成され ます、値のます。強制は、特定のタイプと目的のタイプの値を取得し、元の値と同等の意味を持つ目的のタイプの値を作成しようとします。強制により、タイプが変更されるだけでなく、表現も変更される可能性があります。したがって、強制は必ずしもオブジェクトのアイデンティティを保持するわけではありません。

強制には、情報を失うことのない拡大と、情報を失う可能性のある縮小の2種類があります。拡大強制の例は、32ビットの符号付き整数である値を64ビットの符号付き整数である値に強制することです。ナローイング強制の例はその逆です。64ビットの符号付き整数を32ビットの符号付き整数に強制します。プログラミング言語は、多くの場合、暗黙の変換として拡大強制を実装しますが、縮小強制は通常、明示的な変換を必要とします。

一部の強制は、組み込み型のVES操作に直接組み込まれています(§I.12.1を参照)。他のすべての強制は明示的に要求されるものとします。組み込み型の場合、CTSは、操作のセマンティクスに従って、ランタイムチェックなしで拡張強制を実行し、ランタイムチェックまたは切り捨てを使用して強制を狭める操作を提供します。

I.8.3.3キャスティング

値は複数のタイプである可能性があるため、値を使用するには、どのタイプが使用されているかを明確に識別する必要があります。値は入力された場所から読み取られるため、使用される値のタイプは、値が読み取られた場所のタイプです。別のタイプを使用する場合、値は他のタイプの1つにキャストされます。キャストは通常​​、コンパイル時の操作ですが、コンパイラが値がターゲットタイプであることを静的に認識できない場合は、ランタイムキャストチェックが実行されます。強制とは異なり、キャストはオブジェクトの実際のタイプを変更したり、表現を変更したりすることはありません。キャストはオブジェクトのアイデンティティを保持します。

たとえば、特定のインターフェイスの値を保持するものとして入力された場所から読み取られた値をキャストするときに、ランタイムチェックが必要になる場合があります。インターフェイスは値の不完全な記述であるため、その値を別のインターフェイスタイプにキャストすると、通常、実行時のキャストチェックが行われます。


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の言葉に同意します。

一方、強制は、新しいタイプのメモリに新しいオブジェクトを作成し、元のタイプを新しいオブジェクトにコピーして、両方のオブジェクトをメモリに残します(ガベージコレクターがいずれかまたは両方を削除するまで) 。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.