Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# .NET实例方法内部的this==null-为什么可能?_C#_.net - Fatal编程技术网

C# .NET实例方法内部的this==null-为什么可能?

C# .NET实例方法内部的this==null-为什么可能?,c#,.net,C#,.net,我一直认为,这个在实例方法体中不可能为null。下面的简单程序说明了这是可能的。这是记录在案的行为吗 class Foo { public void Bar() { Debug.Assert(this == null); } } public static void Test() { var action = (Action)Delegate.CreateDelegate(typeof (Action), null, ty

我一直认为,
这个
在实例方法体中不可能为null。下面的简单程序说明了这是可能的。这是记录在案的行为吗

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,但使用它的方法中没有任何内容)。那么这个方法呢 调用另一个使用此点的方法并抛出 例外,接着是一点擦伤。在他们弄明白之后 他们给我们发了一张纸条。 我们认为,能够在空实例上调用方法是一种挑战 有点奇怪。Peter Golde进行了一些测试,以了解性能的影响 总是使用callvirt,而且它足够小,所以我们决定 改变


如果使用call IL指令或委托方法,请确保可以调用方法。只有在尝试访问成员字段时,您才会设置此陷阱,这将为您提供您所寻求的NullReferenceException

试一试

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

例如,String类有这样的检查。它们没有什么神秘之处,只是在像C#这样的语言中你看不到它们的必要性


因为您正在将
null
传递到
Delegate.CreateDelegate的
firstArgument

因此,您正在对空对象调用实例方法

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

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

如果方法和类型的签名匹配(即所有参数 类型是兼容的),则表示委托在 空引用。调用委托就像调用实例一样 方法,这对于 做


请尝试
Delegate.CreateDelegate()
的文档


您正在“手动”调用所有内容,因此没有为
this
指针传递实例,而是传递null。所以这是可能发生的,但是你必须非常努力。

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

您可能会问为什么没有抛出
NullReferenceException
。CLR引发该异常的情况的完整列表如下所示。您的案例未列出。是的,它是一个
callvirt
,但是要
委托。调用
(),而不是
,因此
这个
引用实际上是您的非空委托

您看到的行为对于CLR有一个有趣的实现结果。代理具有一个
目标
属性(对应于您的
引用),该属性经常为
,即当代理为静态时(想象
为静态)。现在,很自然地,该属性有一个私有支持字段,称为
\u target
\u target
是否包含静态委托的空值?它包含对委托本身的引用。为什么不空?因为null是委托的合法目标,如您的示例所示,CLR没有两种类型的
null
指针来区分静态委托

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

<>早期CLR有一个雄心勃勃的计划,包括,甚至是对发誓的C++开发者的选择平台,一个目标是先用托管C++然后用C++ +CLI来实现的。一些过于具有挑战性的语言特性被忽略了,但是没有支持实例执行的实例方法的挑战,这在C++中是完全正常的。包括代表支持

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


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

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


请参阅我的答案,以了解CLR是在.NET1.0中故意设计成这样的。您引用的这篇文章是关于一种不同的(非委托)情况的,这将是一种编译器优化—当实例的类型可以静态确定时,用直接调用替换
callvirt
。请注意,CLR在
callvirt
期间必须抛出
NullReferenceException
的原因是需要基于
引用进行VMT查找;不仅仅是检查引用的语义需求。相关:IMHO,不幸的是,没有标准的方法(可能通过属性)来指定方法应该通过
调用而不是
调用callvirt
来调用,因为这将允许封装
 int x;
 public void Bar()
 {
        x = 1; // NullRefException
        Debug.Assert(this == null);
 }
// 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);
}
this == null // readonly - possible
this = new this() // write - not possible