什么是幕后的演员
可能重复:
C#“as”演员与经典演员
我想知道当我做类似的事情时,.Net CLR的内幕会发生什么
对象myObj =“abc”; string myStr =(string)myObj;
以及第二行如何与string myStr = myObj.ToString()
或string myStr = myObj as string;
环顾四周我找到了generics的答案,比如“编译器在那里插入代码”,但我并不满意……我正在寻找演员机制的深刻内容……哦,编译器插入代码? 给我看看! 编译器优化代码? 怎么样? 什么时候?
请尽可能接近金属!
您可以使用IL Dissasembler在较低级别查看代码生成的内容。 如果您的计算机上安装了Visual Studio,则只需在Windows搜索框中键入“ildasm”即可找到它。
以下是以下代码的IL:
object myObj = "abc"; string myStr = (string)myObj; string tostring = myObj.ToString();
强制转换主要是编译时构造。 这是告诉编译器的方式,“我比你知道的更好,你认为这个实例是这样的,而且这种类型实际上是*其他类型的*。只是假装它真的是另一种类型,让我使用全部另一种类型的方法/属性/字段/等。
在运行时很少有变化。 几乎唯一添加的是检查以确保实例确实属于您尝试转换的类型,如果不是,它将抛出exception。 它或多或少:
if(myObj.GetType() != typeof(string)) throw new ClassCastException();
至于ToString
,它只是一个返回string
的方法。 它在string
类中的定义很可能只是return this;
。 在任何情况下,你不是在技术上投射任何东西,你只是调用一个每个对象都返回一个字符串的方法,当这个对象是一个string
时,你碰巧得到同样的对象。 关键是编译器知道方法调用的结果总是一个字符串,所以不需要做任何特殊的事情。
您可以使用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
它只是回归自己。
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。
另外两种方法是使用is和as作为运算符
是运算符:如果转换成功,则返回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 Operator:返回对已检查对象类型的引用,else返回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运算符时会有轻微的性能提升。
这就是CLR在铸造类型方面所提供的一切。
说到你的代码:
object str =“abc”;
str.ToString()将调用System.object类的ToString方法,该方法是一个虚方法。 调用虚方法时,CLR实际上会检查调用者指向的对象的类型。
这里str实际上是指向一个字符串对象。 因此编译器将生成代码来调用String类的ToString方法,该方法将打印“abc”作为输出。 这个概念是多态的,其中当调用任何类型的虚方法时,实际的对象类型首先由CLR获取,然后适当的方法在对象的正确类型上调用为您的情况下的String类型。