C# 为什么这样做有效?在没有实例的情况下从IL执行方法

C# 为什么这样做有效?在没有实例的情况下从IL执行方法,c#,.net,il,C#,.net,Il,我看了一遍,这段代码让我想了想: public class Program { delegate void HelloDelegate(Strange bar); [STAThread()] public static void Main(string[] args) { Strange bar = null; var hello = new DynamicMethod("ThisIsNull", type

我看了一遍,这段代码让我想了想:

public class Program
{
    delegate void HelloDelegate(Strange bar);

    [STAThread()]
    public static void Main(string[] args)
    {
        Strange bar = null;
        var hello = new DynamicMethod("ThisIsNull",
            typeof(void), new[] { typeof(Strange) },
         typeof(Strange).Module);
        ILGenerator il = hello.GetILGenerator(256);
        il.Emit(OpCodes.Ldarg_0);
        var foo = typeof(Strange).GetMethod("Foo");
        il.Emit(OpCodes.Call, foo);
        il.Emit(OpCodes.Ret);
        var print = (HelloDelegate)hello.CreateDelegate(typeof(HelloDelegate));
        print(bar);
        Console.ReadLine();
    }

    internal sealed class Strange
    {
       public void Foo()
       {
           Console.WriteLine(this == null);
       }
    }
}
我确实理解代码的作用,但我不理解它为什么工作。这不就像做
null.Foo()
?它的工作原理好像
Foo()
是静态的,而这是被调用的:
奇怪的.Foo()


你能告诉我我缺少什么吗?

这个
是作为一个普通参数实现的;您的代码只是将
null
作为此参数传递

这通常会引发
NullReferenceException
的唯一原因是,通常使用
CallVirt
指令调用方法,该指令对
this
参数执行vtable查找,并在其为null时引发


如果使用
调用
,即使
为空,该方法也会执行得非常好,尽管该方法本身可能会在以后抛出。

这非常有趣!非常感谢您的回答:)我不知道
opcode.Callvirt
opcode.Call
之间的区别。一个小小的搜索引导我做到了这一点:事实上,在上面的代码中,更改
il.Emit(OpCodes.Call,foo)
il.Emit(opcode.Callvirt,foo)引发异常。再次感谢您+1.如果有兴趣,可以更详细地讨论使用
callvirt
的一些原因。顺便说一句,这就是编译器使用
call
实现
base.Foo()的原因调用在技术上是违反规范的:C#规范要求
base.Foo()
如果
this==null
则抛出
NullReferenceException
。(当然,如果有人真的想解决这个问题,那么解决办法可能是修改规范。)@hvd:this
有没有任何规范合法的方法可以让它
null
?@SLaks当然,可以从用C#以外的语言实现的另一种方法调用它。C#规范不要求使用非C语言编写的方法来遵守C#的规则,.NET的一大特点是支持不同语言之间的互操作性。这个问题是获取
This==null
的一种合法方法。