What is a cast under the hood [duplicate]

2019-04-24 01:54发布

Possible Duplicate:
C# “as” cast vs classic cast

I want to know what happens under the hood of the .Net CLR when I do something like

object myObj = "abc";
string myStr = (string)myObj;

and how the second line differs from string myStr = myObj.ToString() or string myStr = myObj as string;

looking around I found generics answers such as "the compiler inserts code there" but I'm not satisfied... I'm looking for deep undertanding of the cast mechanics... Oh the compiler inserts code? Show me! The compiler optmizes the code? How? When?

Pls get as close to the metal as you can!!!

标签: c# .net clr
4条回答
We Are One
2楼-- · 2019-04-24 01:57

You can use IL Dissasembler to see what is being produced by your code on a lower level. If you have Visual Studio installed on your machine, you should be able to find it just by typing "ildasm" in the windows search box.

Here's what the IL of the following code looks like:

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

enter image description here

查看更多
smile是对你的礼貌
3楼-- · 2019-04-24 02:03

A cast is primarily a compile time construct. It is your way of telling the compiler, "I know better than you, this instance that you think is such and such a type is * actually* of this other type. Just pretend that it's really this other type and let me use all of the methods/properties/fields/etc. of that other type.

At runtime very little changes. Pretty much the only thing that's added is a check to make sure that the instance really is of the type you were trying to cast to, and if it wasn't it will throw an exception. It's more or less:

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

As for ToString, it's merely a method that returns a string. It's definition within the string class is most likely just return this;. In any case, you're not technically casting anything, you're just calling a method that every object has that returns a string, and when that object is a string under the hood you just happen to get that same object back. The key is that the compiler knows that the result of the method call is always a string, so nothing special needs to be done.

查看更多
来,给爷笑一个
4楼-- · 2019-04-24 02:05

You can use ILDASM, which comes with Visual Studio. It is an IL Disassembler.

Here is the code:

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

Here is what I got:

.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

With ToString():

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

The IL is:

.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

When you cast the string "abc" is stored in location 0 then location 0 is loaded the value is cast using "cast class [mscorlib]System.String" then that value is stored into location 1

When you call .ToString() it calls System.Object.ToString() on the string virtually. ToString() is defined in System.String

Here is the ILDASM for System.String.ToString():

.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

It just returns itself.

查看更多
你好瞎i
5楼-- · 2019-04-24 02:09

CLR allows you to cast an object to its type or any of it's base type. A cast to base type is already considered safe and is implicit.

for eg.

Object s = new String();

But the CLR wants you to specify explicit cast when casting to a derived type

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

Now here what happens it s is not converted to string, but checked whether is of type String or not. If it is found to be of type String the cast is success else an InvalidCastExcetion is thrown.

Two more ways to case are using is and as operators

is Operator : This returns true if the cast succeeds else false.

e.g.

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

So before calling any method, normally you will write code like this

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

This is a little expensive because CLR casts twice.

To avoid this we have the as operator.

as Operator : Returns the reference to the type of object checked else returns null.

e.g. :

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
}

So you can see, there is a slight performance boost when using as operator.

That is all what the CLR has to offer when it comes to casting types.


When it comes to your code :

object str = "abc";

str.ToString() will call the ToString method of the System.object class which is a virtual method. When calling a virtual method, CLR will actually check the type of the object pointed by the caller.

Here str is actually pointing to a string object. So compiler will generate code to call the ToString method of the String class which will print the "abc" as output. This concept is polymorphism in which when calling a virtual method of any type, the actual object type is first obtained by the CLR and then appropiate method is called on the correct type of the object as String type in your case.

查看更多
登录 后发表回答