我一直认为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
答案 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.Invoke
(see 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 reading和even better reading显示允许空实例的设计,即使在非常自然的C#语法上下文中也会显示其痕迹。
答案 4 :(得分:0)
这是C#类中的只读引用。因此,正如预期的那样,它可以像任何其他参考文献一样使用(在只读模式下)......
this == null // readonly - possible
this = new this() // write - not possible