(this == null)在C#中!


129

由于C#4中已修复的错误,因此以下程序打印true。(在LINQPad中尝试)

void Main() { new Derived(); }

class Base {
    public Base(Func<string> valueMaker) { Console.WriteLine(valueMaker()); }
}
class Derived : Base {
    string CheckNull() { return "Am I null? " + (this == null); }
    public Derived() : base(() => CheckNull()) { }
}

在VS2008中,在发布模式下,它将引发InvalidProgramException。(在调试模式下,它可以正常工作)

在VS2010 Beta 2中,它不会编译(我没有尝试过Beta 1)。我了解到这很难

还有其他任何方法可以this == null用纯C#制作吗?


3
这很可能是C#3.0编译器中的错误。它以C#4.0中的方式工作。
Mehrdad Afshari

82
@SLaks:错误的问题在于,您可以期望它们在某个时候被修复,因此发现它们“有用”可能是不明智的。
AnthonyWJones

6
谢谢!不知道LINQPad。这很酷!
索恩

8
究竟以什么方式有用?
艾伦·赖斯2009年

6
这个错误有什么用?
BlackTigerX

Answers:


73

该观察结果已在今天早些时候的另一个问题中发布在StackOverflow 上。

Marc 对这个问题很好回答表明,根据规范(第7.5.7节),您不应this在那种情况下进行访问,并且在C#3.0编译器中进行访问的能力是一个错误。C#4.0编译器根据规范正确运行(即使在Beta 1中,这也是编译时错误):

§7.5.7此访问

一个this访问由保留字的this

此访问:

this

此访问仅在允许的的实例构造函数,实例方法或实例访问。


2
我看不到,为什么在此问题给出的代码中,关键字“ this”的使用无效。该方法CheckNull是正常实例方法,非静态。在这种方法中使用“ this”是100%有效的,甚至将此与null进行比较也是有效的。错误在基本初始化行中:尝试将实例绑定的委托作为参数传递给基本ctor。这是编译器中的错误(语义检查中的一个漏洞):应该是不可能的。: base(CheckNull())如果CheckNull不是静态的,则不允许您写,并且同样,您不能内联实例绑定的lambda。
quetzalcoatl 2012年

4
@quetzalcoatl:thisin CheckNull方法是合法的。什么是不合法的是隐含的 这个访问() => CheckNull(),基本上() => this.CheckNull(),这是外界运行实例构造函数。我同意我引用的规范部分主要集中在this关键字的句法合法性上,也许另一部分可以更精确地解决这个问题,但是从概念上也很容易从规范的这一部分进行推断。
Mehrdad Afshari'8

2
对不起,我不同意。虽然我知道这一点(并在上面的评论中写道),但是您也知道-您(接受的)答案中没有提到问题的真正原因。答案是可以接受的-因此貌似作者也掌握了答案。但是我怀疑所有读者是否会一视同仁地理解lambda,一眼就能认出instancebound-lambda与static-lambda并将其映射到“ this”和发出的IL问题:)这就是为什么我加三分钱。除此之外,我同意您和其他人发现,分析和描述的所有其他内容:)
quetzalcoatl

24

调试模式二进制文件的原始反编译(无优化的Reflector)为:

private class Derived : Program.Base
{
    // Methods
    public Derived()
    {
        base..ctor(new Func<string>(Program.Derived.<.ctor>b__0));
        return;
    }

    [CompilerGenerated]
    private static string <.ctor>b__0()
    {
        string CS$1$0000;
        CS$1$0000 = CS$1$0000.CheckNull();
    Label_0009:
        return CS$1$0000;
    }

    private string CheckNull()
    {
        string CS$1$0000;
        CS$1$0000 = "Am I null? " + ((bool) (this == null));
    Label_0017:
        return CS$1$0000;
    }
}

CompilerGenerated方法没有任何意义;如果您查看IL(下图),它将在空字符串(!)上调用该方法。

   .locals init (
        [0] string CS$1$0000)
    L_0000: ldloc.0 
    L_0001: call instance string CompilerBug.Program/Derived::CheckNull()
    L_0006: stloc.0 
    L_0007: br.s L_0009
    L_0009: ldloc.0 
    L_000a: ret 

在发布模式下,对局部变量进行了优化,因此它尝试将不存在的变量压入堆栈。

    L_0000: ldloc.0 
    L_0001: call instance string CompilerBug.Program/Derived::CheckNull()
    L_0006: ret 

(将其转换为C#时,反射器会崩溃)


编辑:有人(Eric Lippert吗?)知道为什么编译器会发出ldloc吗?


11

我有那个!(也有证据)

替代文字


2
已经晚了,这是我应该停止编码的迹象了:)正在用DLR东西IIRC入侵我们。
leppie

使调试器可视化器(DebuggerDisplay)表示“ this”是什么,然后使您傻乎乎的是null?:D只说了

10

这不是一个“ bug”。这是您滥用类型系统。您绝不应该将对当前实例(this)的引用传递给构造函数内的任何人。

我也可以通过在基类构造函数中调用虚拟方法来创建类似的“ bug”。

仅仅因为您可以做坏事并不意味着当您被它咬伤时就发现了它的错误


14
这是一个编译器错误。它生成无效的IL。(请阅读我的回答)
Slaks

上下文是静态的,因此在该阶段不应允许您引用实例方法。
leppie

10
@Will:这是一个编译器错误。编译器应该为该代码段生成有效的可验证的代码,或吐出一条错误消息。当编译器不按照规范运行时,它就是错误的
Mehrdad Afshari

2
@ Will#4:当我编写代码时,我还没有考虑过影响。我只是意识到,当它在VS2010中停止编译时,这没有任何意义。–
SLaks

3
顺便说一句,构造函数中的虚拟方法调用是完全有效的操作。只是不推荐。它可能导致逻辑灾难,但永远不会造成灾难InvalidProgramException
Mehrdad Afshari,2009年

4

我可能是错的,但是我很确定您的对象是否null存在永远不会this适用的情况。

例如,您怎么称呼CheckNull

Derived derived = null;
Console.WriteLine(derived.CheckNull()); // this should throw a NullReferenceException

3
在构造函数参数中的lambda中。阅读整个代码片段。(如果您不相信我,请尝试一下)
拍了

我同意,尽管我确实记得有关C ++的对象在其构造函数中没有引用的隐隐约约的东西,但我想知道在这种情况下是否使用(this == null)方案检查对方法的调用是否由对象的构造函数完成,然后将指针公开给“ this”。不过,据我在C#中所知,即使在Dispose或finalization方法中,也不应该存在“ this”为null的任何情况。
jpierson

我想我的意思是,的想法this完全排除了可能为null的可能性,这是计算机编程的“可疑,易碎的总和”。因此,您渴望使用该表达式this == null并使其返回真值的愿望使我误入歧途。
丹涛

换句话说:我确实读过您的代码;我的意思是,我首先质疑您要完成的任务。
丹涛

这段代码仅演示了该错误,并且正如您所指出的那样,完全没有用。要查看真正有用的代码,请阅读第二个答案。
SLaks

-1

不确定这是否是您要找的东西

    public static T CheckForNull<T>(object primary, T Default)
    {
        try
        {
            if (primary != null && !(primary is DBNull))
                return (T)Convert.ChangeType(primary, typeof(T));
            else if (Default.GetType() == typeof(T))
                return Default;
        }
        catch (Exception e)
        {
            throw new Exception("C:CFN.1 - " + e.Message + "Unexpected object type of " + primary.GetType().ToString() + " instead of " + typeof(T).ToString());
        }
        return default(T);
    }

示例:UserID = CheckForNull(Request.QueryString [“ UserID”],147);


13
完全误解了这个问题。
Slaks 2010年

1
我想了很多。以为我还是会尝试。
Scott和开发团队
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.