8

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

我想知道当我做类似的事情时.Net CLR 的底层会发生什么

对象 myObj = "abc";
字符串 myStr = (string)myObj;

以及第二行与string myStr = myObj.ToString()或有何不同string myStr = myObj as string;

环顾四周,我发现了诸如“编译器在其中插入代码”之类的泛型答案,但我不满意……我正在寻找对演员表机制的深入了解……哦,编译器插入了代码?给我看看!编译器优化代码?如何?什么时候?

请尽可能靠近金属!

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

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.

于 2012-12-27T19:47:52.223 回答
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

它只是返回自己。

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

现在在这里发生的事情不是将它转换为字符串,而是检查是否是字符串类型。如果发现它是 String 类型,则强制转换成功,否则抛出 InvalidCastExcetion。

另外两种 case 方法是使用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 会强制转换两次。

为了避免这种情况,我们有as运算符。

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

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

这就是 CLR 在转换类型时必须提供的全部内容。


当涉及到您的代码时:

对象 str = "abc";

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

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

于 2012-12-27T20:07:18.203 回答