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

时间:2012-05-16 19:25:48

标签: c# .net

我一直认为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();
}

更新

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

  

我们收到了某人的报告(可能是一个.NET小组   使用C#(当时认为它还没有命名为C#))   在空指针上调用方法的编写代码,但它们没有   得到一个例外,因为该方法没有访问任何字段(即   “this”为null,但该方法中没有使用它)。那个方法呢   叫做另一种方法,确实使用了这一点并扔了一个   例外,随之而来的是一些令人头疼的问题。在他们想出来之后   他们给我们发了一张关于它的说明。   我们认为能够在null实例上调用方法是一个   有点奇怪。彼得戈尔德做了一些测试,看看性能影响   总是使用callvirt,它足够小,我们决定   做出改变。

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

5 个答案:

答案 0 :(得分:23)

因为您将null传递到firstArgument的{​​{1}}

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

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

  

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

     

如果类型的签名明确包含隐藏的第一个   方法的参数,代表被称为开放   实例方法。调用委托时,第一个参数在   参数列表传递给隐藏的实例参数   方法

     

如果方法和类型的签名匹配(即所有参数   类型是兼容的),然后代表被关闭a   空引用。调用委托就像调用实例一样   在null实例上的方法,这不是一个特别有用的东西   做。

答案 1 :(得分:12)

当您使用调用IL指令或委托方法时,确定可以调用方法。如果您尝试访问将为您提供您所寻求的NullReferenceException的成员字段,则只会关闭此booby陷阱。

 int x;
 public void Bar()
 {
        x = 1; // NullRefException
        Debug.Assert(this == null);
 }

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

例如,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);
}

答案 2 :(得分:5)

试用Delegate.CreateDelegate() at msdn的文档。

你“手动”调用所有内容,因此不是传递this指针的实例,而是传递null。所以它可能会发生,但你必须非常努力地尝试。

答案 3 :(得分:5)

this是一个引用,因此从类型系统的角度看它是null没有问题。

您可能会问为什么NullReferenceException没有被抛出。 CLR抛出该异常的完整情况列表为documented。您的案例未列出。是的,它 callvirt,但是Delegate.Invokesee here)而不是Bar,所以this参考实际上是你的非空委托!

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

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

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

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

More good readingeven better reading显示允许空实例的设计,即使在非常自然的C#语法上下文中也会显示其痕迹。

答案 4 :(得分:0)

这是C#类中的只读引用。因此,正如预期的那样,它可以像任何其他参考文献一样使用(在只读模式下)......

this == null // readonly - possible
this = new this() // write - not possible