C#一直被认为是强类型且类型安全的语言。但是,最近在调试一个关于IL Emit的问题时候,却发现,一直认为的C#类型安全,并不是那么“安全”。
public static void Foo(string str)
{
Console.WriteLine(str.GetType());
}
上面这段代码的输出,通常都认为是System.String,最多还能让他抛出个NullReferenceException
。但是实际上,他可以输出其他东西。
我这里让他输出了一个System.Int32出来。我给一个string参数传进去了一个int,但是并没有出现InvalidCastException
,而是代码正常执行了。通常意义上认为的C#的类型安全似乎不存在了。
那么这个CastTo<string>
干了什么呢?他的代码如下:
从反编译的C#来看,这是有语法错误的。这部分代码的确是C#自身写不出来的,只能通过IL写出来,然后C#就可以调用。从IL来看,就是直接把参数当做结果返回了,并且忽略了参数的类型。所以类型不安全的其实是IL,但是IL的代码可以让C#直接用,反而把IL也变得类型不安全了。
这种情况的代码就真正没有办法保证类型安全吗?其实CLR还有有一层机制,叫做代码验证,下面是我从MSDN的截图:
而默认的安全策略是允许非类型安全的代码执行,所以我们的代码执行了。如果强制用Peverify进行代码验证话,会提示我们的代码并不是类型安全的,如下图。
最后附上几个用非类型安全的代码干的有意思的事情。
这里的0是因为object里并没有对应的字段,指向的字段的位置根据CLR的内存布局正好是0罢了。
拿到了指向对象的内存指针,所以null就是0了。
这里结果正好就是0x0000000100000001,由于CLR内存布局的原因,将两个32位的字段合在一起当做一个64位的字段了。