关于.net:对象构造函数可以返回null吗?

Can object constructor return a null?

我们已经接管了一些.NET 1.1 Windows服务代码,这些代码生成线程来从队列中读取消息(请参见egate jms队列,但这并不重要),然后生成线程来处理目标应用程序服务中的消息。我们不断地遇到逻辑和设计决策,这让我们困惑不已。下面是一个示例,其中消息(lsmessage)已从队列中检索并准备好进行处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
if(lsMessage != null)
{
    // Initialize a new thread class instance, pass in message
    WorkerThread worker = new WorkerThread(lsMessage);

Process:
    // Start a new thread to process the message
    Thread targetWorker = new Thread(new ThreadStart(worker.ProcessMessage));
    if(targetWorker != null)
    {
        targetWorker.Priority = ThreadPriority.Highest;
        targetWorker.Name ="Worker" + queueKey.ToString();
        targetWorker.Start();

        // wait for worker thread to join back in specified period
        bool isFinished = targetWorker.Join(SYNC_THREAD_TIMEOUT);

        string message = worker.replyMsg;

        if ( !isFinished )  // BF is timeout
        {
            targetWorker.Abort();

            // [obscure developer name] 25/10/2004: calling Join() to wait for thread to terminate.
            // for EAI listener threads problem, ensure no new thread is started
            // before the old one ends
            targetWorker.Join();

            // prepare reply message
            string errorMsg = string.Format("EAIMsg {0}: BF is timeout. Send sync message back to caller.", worker.messageKey);
            log.Debug(errorMsg);

            message = worker.GenErrorCode(message, errorMsg);
        }

        // Commit message
        MQ.ReceiverCommit(queueKey, worker.messageKey, false);

        // Send back the response to the caller
        MQ.RespondSend(queueKey, message);
    }
    else
    {
        log.Debug(string.Format("Fail to start worker thread to process sync message. Thread returned is null. Sleep for {0} milliseconds.", LIMIT_RESOURCE_SLEEP));
        Thread.Sleep(LIMIT_RESOURCE_SLEEP);
        goto Process;
    }
}

请忽略标签的使用,暂时转到;这不是问题。我们的困惑是在实例化之后检查线程对象是否为空。下面的else语句似乎表明以前的开发人员遇到过类似的情况。当然,最初的开发人员早就不见了。所以我们想知道,在调用构造函数并返回一个空值之后,clr真的能实例化一个对象吗?我们不知道这种可能性。


在我看来,else声明所暗示的是,以前的开发人员不知道他们的C。构造函数总是返回构造的对象或引发异常。

在过去的年代,C++构造函数可以返回EDCOX1×0,因此问题可能来自于此。这在C++中也不再是正确的,至少对于默认的EDOCX1·7Ω操作符来说是这样。


编辑:为了澄清这一点,有一个疯狂的边缘案例,你可以从类构造函数中获得null,但坦率地说,我认为任何真正的代码都不应该期望处理这种疯狂的水平:你在C或.NET中看到的最奇怪的角案例是什么?.所有正常的意图:它不会发生。

不,不能从类构造函数(Thread是类)中获取空值。我所知道的唯一一个构造函数可以(似乎)返回null的情况是Nullable—也就是说。

1
object foo = new int?(); // this is null

这是仿制药的一个稍大的问题:

1
2
3
4
5
6
7
8
static void Oops<T>() where T : new() {
    T t = new T();
    if (t == null) throw new InvalidOperationException();
}

static void Main() {
    Oops<int?>();
}

(当然,也有检查/处理这种情况的方法,如: class)

除此之外,构造函数总是返回一个对象(或初始化一个结构),或者抛出一个异常。


正如core所提到的,运算符重载可能使构造函数返回null,而实际情况并非如此。这篇文章的作者core发现,他们没有看到它被使用过,但实际上它被用于一个非常流行的产品:Unity。

这将在运行时编译和记录消息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
using UnityEngine;

public class Test:MonoBehaviour
{
    void Start()
    {
        AudioSource as = new AudioSource();

        if (as == null)
        {
            Debug.Log("Looks null to me.");
        }
    }
}

现在,这里的错误是我的,因为不应该直接调用AudioSource构造函数。但是应该知道,由于对象统一可以引用,因此继承树的根目录中的==操作符被重载。以下是Unity手册中关于UnityEngine.Object==操作员的内容:

Be careful when comparing with null.

e.g.

1
2
3
4
5
GameObject go = new GameObject();
Debug.Log (go == null); // false

Object obj = new Object();
Debug.Log (obj == null); // true

Instatiating a GameObject adds it to the scene so it's completely
initialized (!destroyed). Instantiating a simple UnityEngine.Object
has no such semantics, so the(sic) it stays in the 'destroyed' state which
compares true to null.

当实例化一个GameObject初始化它时,实例化一个AudioSource对象不会,因此与null的比较返回true

由于试图引用未初始化的AudioSource对象的属性会引发空引用异常,这一不寻常的习惯用法变得更加隐秘,我最初错误地解释为对象引用是null而不是属性。

其他人回答了OP的问题,但我想补充这个答案,因为如果其中的Thread类不是我们所期望的那样,那么OP的代码实际上可能是有意义的,正如Object类(及其后代)不是你所期望的那样,它在一个统一脚本中(也就是说,它实际上是UnityEngine.Object类,而是比System.Object还要多,这会让你的==操作员超载,这让我很困惑)。


可以使其看起来像对象ctor返回空值:

Returning null from a class constructor?!

搜索"另一个我没见过使用的模式允许一个无效对象模拟一个空引用",然后从中读取。


不!空检查是多余的。很多搬到C++的C++开发者都有一个空校验的习惯,我猜这里是一样的。

唯一的事情是,您应该检查文档,看看构造函数是否可以抛出任何异常。在您的情况下,请参阅http://msdn.microsoft.com/en-us/library/xx3ezzs2.aspx,如前所述,构造函数将始终返回有效的obj。