什么是引擎盖下的演员

时间:2012-12-27 19:38:05

标签: c# .net clr

  

可能重复:
  C# “as” cast vs classic cast

当我做

这样的事情时,我想知道.Net CLR的内幕会发生什么
object myObj = "abc";
string myStr = (string)myObj;

以及第二行与string myStr = myObj.ToString()string myStr = myObj as string;

的区别 环顾四周,我找到了泛型答案,例如“编译器在那里插入代码”,但我并不满意......我正在寻找演员机制的深刻内容......哦,编译器插入代码?给我看看!编译器优化代码?怎么样?当α

请尽可能接近金属!!!

4 个答案:

答案 0 :(得分:3)

您可以使用IL Dissasembler查看代码在较低级别生成的内容。如果您的计算机上安装了Visual Studio,则只需在Windows搜索框中键入“ildasm”即可找到它。

以下是以下代码的IL:

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

enter image description here

答案 1 :(得分:2)

强制转换主要是编译时构造。这是告诉编译器的方式,“我比你更了解,你认为这个实例是这样的,这种类型实际上是*这个其他类型的*。只是假装它真的是另一种类型,让我使用全部其他类型的方法/属性/字段/等。

在运行时很少有变化。几乎唯一添加的是检查以确保实例确实属于您尝试转换的类型,如果不是,它将抛出异常。它或多或少:

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

至于ToString,它只是一个返回string的方法。它在string类中的定义很可能只是return this;。在任何情况下,你不是在技术上投射任何东西,你只是调用一个每个对象都返回一个字符串的方法,当这个对象是一个string时,你碰巧得到同样的对象背部。关键是编译器知道方法调用的结果总是一个字符串,所以不需要做任何特殊的事情。

答案 2 :(得分:1)

您可以使用Visual Studio附带的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

它只是回归自己。

答案 3 :(得分:1)

CLR允许您将对象强制转换为其类型或任何基类型。强制转换为基类型已被认为是安全的并且是隐含的。

例如。

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

现在发生了什么,它没有转换为字符串,但检查是否是String类型。如果发现它是String类型,则转换成功,否则抛出InvalidCastExcetion。

另外两种方法是使用 作为运算符

是运算符:如果强制转换成功,则返回true。

e.g。

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投了两次。

为避免这种情况,我们将设为运算符。

as Operator:返回对所检查对象类型的引用,否则返回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
}

所以你可以看到,使用as operator时会略微提升性能。

这就是CLR在铸造类型时所提供的一切。


说到你的代码:

object str =“abc”;

str.ToString()将调用System.object类的ToString方法,该方法是一个虚方法。调用虚方法时,CLR实际上会检查调用者指向的对象的类型。

这里str实际上是指向一个字符串对象。 因此编译器将生成代码来调用String类的ToString方法,该方法将打印“abc”作为输出。 这个概念是多态,当调用任何类型的虚方法时,实际的对象类型首先由CLR获取,然后在你的情况下,在对象的正确类型上调用适当的方法。