8

重複の可能性:
C#「as」キャストとクラシックキャスト

次のようなことをすると、.NetCLRの内部で何が起こるか知りたいです。

オブジェクトmyObj="abc";
string myStr =(string)myObj;

2行目がまたはとどのように異なるstring myStr = myObj.ToString()string myStr = myObj as string;

周りを見回すと、「コンパイラがコードを挿入する」などのジェネリックスの答えが見つかりましたが、満足していません...キャストメカニズムの深い理解を探しています...コンパイラがコードを挿入しますか?見せて!コンパイラはコードを最適化しますか?どのように?いつ?

plsはあなたができる限り金属に近づきます!!!

4

4 に答える 4

3

IL Dissasemblerを使用して、コードによって生成されているものを下位レベルで確認できます。Visual Studioがマシンにインストールされている場合は、Windowsの検索ボックスに「ildasm」と入力するだけで見つけることができます。

次のコードのILは次のようになります。

object myObj = "abc";
string myStr = (string)myObj;    
string tostring = myObj.ToString();

ここに画像の説明を入力してください

于 2012-12-27T19:45:38.693 に答える
2

キャストは、主にコンパイル時の構成です。これは、コンパイラに「私はあなたよりもよく知っています。あなたがそのようなものだと思うこのインスタンスは、*実際には*この他のタイプです。本当にこの他のタイプであると偽って、すべてを使用させてください。他のタイプのメソッド/プロパティ/フィールドなど。

実行時の変更はほとんどありません。追加されるのは、インスタンスが実際にキャストしようとしたタイプであることを確認するためのチェックだけです。そうでない場合は、例外がスローされます。多かれ少なかれ:

if(myObj.GetType() != typeof(string))
    throw new ClassCastException();

についてToStringは、単に。を返す​​メソッドですstring。クラス内での定義stringはおそらくただreturn this;です。いずれにせよ、技術的には何もキャストしていません。文字列を返すすべてのオブジェクトが持つメソッドを呼び出しているだけです。そのオブジェクトが内部にある場合は、stringたまたま同じオブジェクトを取り戻すことになります。重要なのは、メソッド呼び出しの結果が常に文字列であることをコンパイラーが認識しているため、特別なことを行う必要がないことです。

于 2012-12-27T19:47:52.223 に答える
1

VisualStudioに付属しているILDASMを使用できます。IL逆アセンブラです。

コードは次のとおりです。

public void Foo()
{
    object myObj = "abc";
    string myStr = (string)myObj;
}

これが私が得たものです:

.method public hidebysig instance void  Foo() cil managed
{
  // Code size       15 (0xf)
  .maxstack  1
  .locals init ([0] object myObj,
           [1] string myStr)
  IL_0000:  nop
  IL_0001:  ldstr      "abc"
  IL_0006:  stloc.0
  IL_0007:  ldloc.0
  IL_0008:  castclass  [mscorlib]System.String
  IL_000d:  stloc.1
  IL_000e:  ret
} // end of method ShowWhatCastIs::Foo

ToString()を使用する場合:

public void Foo2()
{
    object myObj = "abc";
    string myStr = myObj.ToString();
}

ILは次のとおりです。

.method public hidebysig instance void  Foo2() cil managed
{
  // Code size       15 (0xf)
  .maxstack  1
  .locals init ([0] object myObj,
           [1] string myStr)
  IL_0000:  nop
  IL_0001:  ldstr      "abc"
  IL_0006:  stloc.0
  IL_0007:  ldloc.0
  IL_0008:  callvirt   instance string [mscorlib]System.Object::ToString()
  IL_000d:  stloc.1
  IL_000e:  ret
} // end of method ShowWhatCastIs::Foo2

文字列「abc」をキャストすると、ロケーション0に格納され、ロケーション0がロードされ、「cast class [mscorlib] System.String」を使用して値がキャストされ、その値がロケーション1に格納されます。

.ToString()を呼び出すと、仮想的に文字列に対してSystem.Object.ToString()が呼び出されます。ToString()はSystem.Stringで定義されています

System.String.ToString()のILDASMは次のとおりです。

.method public hidebysig virtual instance string 
        ToString() cil managed
{
  .custom instance void System.Runtime.TargetedPatchingOptOutAttribute::.ctor(string) = ( 01 00 3B 50 65 72 66 6F 72 6D 61 6E 63 65 20 63   // ..;Performance c
                                                                                          72 69 74 69 63 61 6C 20 74 6F 20 69 6E 6C 69 6E   // ritical to inlin
                                                                                          65 20 61 63 72 6F 73 73 20 4E 47 65 6E 20 69 6D   // e across NGen im
                                                                                          61 67 65 20 62 6F 75 6E 64 61 72 69 65 73 00 00 ) // age boundaries..
  .custom instance void __DynamicallyInvokableAttribute::.ctor() = ( 01 00 00 00 ) 
  // Code size       2 (0x2)
  .maxstack  8
  IL_0000:  ldarg.0
  IL_0001:  ret
} // end of method String::ToString

それはただ自分自身を返します。

于 2012-12-27T19:54:09.367 に答える
1

CLRを使用すると、オブジェクトをその型またはその基本型のいずれかにキャストできます。基本タイプへのキャストはすでに安全であると見なされており、暗黙的です。

たとえば。

オブジェクトs=new String();

ただし、CLRでは、派生型にキャストするときに明示的なキャストを指定する必要があります

String str = s; // Give error and needs you to explicitly cast it
//To get it to work
String str = (String)s;

ここで何が起こるかは文字列に変換されませんが、文字列型であるかどうかがチェックされます。文字列型であることが判明した場合、キャストは成功します。それ以外の場合、InvalidCastExcetionがスローされます。

ケースのもう2つの方法は、 isas演算子を使用しています

is Operator:キャストが成功した場合はtrueを返し、それ以外の場合はfalseを返します。

例えば

Object o = new Object();
Boolean b1 = (o is Object); // b1 is true
Boolean b2 = (o is DateTime); // b2 is false

したがって、メソッドを呼び出す前に、通常は次のようなコードを記述します

if(o is DateTime)  // Check this by observing the object
{
    DateTime d = (DateTime)o; // Again cast the object to obtain a reference 
    // Perform operations
}

CLRは2回キャストするため、これは少し高価です。

これを回避するために、as演算子があります。

as Operator:チェックされたオブジェクトのタイプへの参照を返します。そうでない場合はnullを返します。

例:

DateTime d = o as DateTime; // Check the object to verify the case and returns reference to the object itself or null

if(d != null)
{
   // Perform the operations
}

ご覧のとおり、演算子として使用するとパフォーマンスがわずかに向上します。

鋳造タイプに関しては、CLRが提供するのはこれだけです。


あなたのコードに関しては:

オブジェクトstr="abc";

str.ToString()は、仮想メソッドであるSystem.objectクラスのToStringメソッドを呼び出します。仮想メソッドを呼び出すとき、CLRは実際に呼び出し元が指すオブジェクトのタイプをチェックします。

ここで、strは実際には文字列オブジェクトを指しています。したがって、コンパイラは、「abc」を出力として出力するStringクラスのToStringメソッドを呼び出すコードを生成します。この概念はポリモーフィズムであり、任意のタイプの仮想メソッドを呼び出すときに、実際のオブジェクトタイプが最初にCLRによって取得され、次に適切なメソッドがオブジェクトの正しいタイプで文字列タイプとして呼び出されます。

于 2012-12-27T20:07:18.203 に答える