这个== null在.NET实例方法中 - 为什么这可能?

emp*_*mpi 34 .net c#

我一直认为this在实例方法体内是不可能的.以下简单的程序表明它是可能的.这是一些记录在案的行为?

class Foo
{
    public void Bar()
    {
        Debug.Assert(this == null);
    }
}

public static void Test()
{            
    var action = (Action)Delegate.CreateDelegate(typeof (Action), null, typeof(Foo).GetMethod("Bar"));
    action();
}
Run Code Online (Sandbox Code Playgroud)

UPDATE

我同意答案,说明这种方法的记录方式.但是,我真的不明白这种行为.特别是因为它不是C#的设计方式.

我们收到了某人的报告(很可能是一个使用C#的.NET组(当时认为它还没有命名为C#))谁编写了一个在空指针上调用方法的代码,但是他们没有得到一个例外,因为该方法没有访问任何字段(即"this"为null,但方法中没有使用它).那个方法然后调用另一个方法,它确实使用了这一点并引发了一个异常,随后出现了一些令人头疼的问题.在他们弄清楚之后,他们给我们发了一张关于它的说明.我们认为能够在null实例上调用方法有点奇怪.Peter Golde做了一些测试,看看总是使用callvirt的性能影响是什么,它足够小,我们决定做出改变.

http://blogs.msdn.com/b/ericgu/archive/2008/07/02/why-does-c-always-use-callvirt.aspx

Mar*_*wul 23

因为你传递nullfirstArgumentDelegate.CreateDelegate

所以你要在null对象上调用实例方法.

http://msdn.microsoft.com/en-us/library/74x8f551.aspx

如果firstArgument是空引用而method是实例方法,则结果取决于委托类型类型和方法的签名:

如果类型的签名明确包含方法的隐藏的第一个参数,则表示委托表示开放实例方法.调用委托时,参数列表中的第一个参数将传递给方法的隐藏实例参数.

如果方法和类型的签名匹配(即,所有参数类型都是兼容的),则表示委托被空引用关闭.调用委托就像在null实例上调用实例方法一样,这不是特别有用的事情.


Alo*_*aus 12

当您使用调用IL指令或委托方法时,您确实可以调用方法.如果您尝试访问将为您提供您所寻求的NullReferenceException的成员字段,则仅设置此booby陷阱.

尝试

 int x;
 public void Bar()
 {
        x = 1; // NullRefException
        Debug.Assert(this == null);
 }
Run Code Online (Sandbox Code Playgroud)

BCL甚至包含显式的这个== null检查,以帮助调试不使用callvirt(如C#)的语言.有关更多信息,请参阅此问题.

例如,String类具有此类检查.除了你不会在C#这样的语言中看到它们的需要之外,没有什么是神秘的.

// Determines whether two strings match. 
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] 
public override bool Equals(Object obj)
{
    //this is necessary to guard against reverse-pinvokes and
    //other callers who do not use the callvirt instruction
    if (this == null)
        throw new NullReferenceException();

    String str = obj as String;
    if (str == null) 
        return false;

    if (Object.ReferenceEquals(this, obj)) 
        return true;

    return EqualsHelper(this, str);
}
Run Code Online (Sandbox Code Playgroud)


Kev*_*son 5

试试Delegate.CreateDelegate() msdn的文档.

你是"手动"调用所有内容,因此不是为this指针传递实例,而是传递null.所以它可能会发生,但你必须非常努力.


Jir*_*ika 5

this是一个参考,所以null从类型系统的角度来看它是没有问题的.

你可能会问为什么NullReferenceException没有抛出.当CLR抛出该异常情况的完整列表记录.您的案例未列出.是的,它一个callvirt,但是Delegate.Invoke(见这里)而不是Bar,所以this引用实际上是你的非null委托!

您看到的行为对CLR有一个有趣的实现后果.委托有一个很常见的Target属性(对应于你的this引用)null,即委托是静态的(想象Bar是静态的).现在,有一个私有支持字段,称为该属性_target.是否_target包含静态委托的null? 不,不. 它包含对委托本身的引用.为什么不null?因为null是委托的合法目标,如您的示例所示,并且CLR没有两种null指针来区分静态委托.

这一部分的trivium表明,对于委托,实例方法的空目标是没有事后的想法.你可能仍在问最终的问题:但为什么必须得到他们的支持?

早期的CLR有一个雄心勃勃的计划,即使对于宣誓的C++开发人员来说,成为首选平台,这个目标首先是使用托管C++,然后是C++/CLI.一些过于具有挑战性的语言特性被省略了,但是在没有实例的情况下支持实例方法执行并没有什么真正的挑战,这在C++中是完全正常的.包括代表支持.

因此,最终的答案是:因为C#和CLR是两个不同的世界.

更好的阅读更好的阅读,以显示允许空实例的设计,即使在非常自然的C#语法上下文中也显示其痕迹.