我们已经接管了一些.NET 1.1 Windows服务代码,它产生线程以从队列中读取消息(SeeBeyond eGate JMS队列,但这并不重要),并反过来产生线程来处理目标应用程序服务中的消息。我们不断遇到令我们困惑的逻辑和设计决策。这是一个示例,其中已从队列中检索消息(lsMessage)并准备好进行处理
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;
}
}
请暂时忽略使用标签和转到;这不是问题。我们的困惑是在实例化后立即检查Thread对象是否为空。下面的else语句似乎表明之前的开发人员之前遇到过类似的情况。当然,原来的开发者早已不复存在。所以我们想知道,在调用构造函数并返回null之后CLR是否真的可以实例化一个对象?我们不了解这种可能性。
答案 0 :(得分:35)
在我看来,else
声明表明以前的开发人员不知道他们的C#。构造函数总是返回构造对象或抛出异常。
在很久以前,C ++构造函数可以返回null
,所以问题可能来自于此。这在C ++中也不再适用,至少对于默认的new
运算符来说是这样。
答案 1 :(得分:25)
编辑:为了澄清,有一个疯狂边缘案例,您可以从类构造函数中获取null
,但坦率地说,我认为任何真正的代码都不应该 / em>期望处理这种疯狂的程度:What's the strangest corner case you've seen in C# or .NET?。对于所有正常意图:它不会发生。
不,你不能从类构造函数中获取null(Thread
是一个类)。我知道构造函数可以(似乎)返回null
的唯一情况是Nullable<T>
- 即。
object foo = new int?(); // this is null
这是泛型的一个稍大的问题:
static void Oops<T>() where T : new() {
T t = new T();
if (t == null) throw new InvalidOperationException();
}
static void Main() {
Oops<int?>();
}
(当然,有检查/处理该方案的方法,例如: class
)
除此之外,构造函数将始终返回一个对象(或初始化一个struct),或抛出一个异常。
答案 2 :(得分:2)
NO!空检查是多余的。许多转向C#的C ++开发人员都有这种无效检查的习惯,我猜这里也是一样的。
唯一的问题是您应该检查文档以查看构造函数是否可以抛出任何异常。在您的情况下,请参考http://msdn.microsoft.com/en-us/library/xx3ezzs2.aspx,如上所述,构造函数将始终返回有效的obj。
答案 3 :(得分:2)
你可以使它看起来像一个对象ctor返回null:
http://seattlesoftware.wordpress.com/2008/03/05/returning-null-from-a-class-constructor/
搜索“我没见过的另一个模式允许无效对象模拟空引用”并从那里读取。
答案 4 :(得分:2)
正如core
所提到的那样,运算符重载可能会使构造函数返回null
,而这不是真正发生的事情。 core
文章的作者发现他们没有看到它的使用,但它实际上用于一个非常受欢迎的产品:Unity。
这将在运行时编译并记录消息:
using UnityEngine;
public class Test:MonoBehaviour
{
void Start()
{
AudioSource as = new AudioSource();
if (as == null)
{
Debug.Log("Looks null to me.");
}
}
}
现在,这里的错误是我的,因为一个不直接调用AudioSource
构造函数。但是一个应知道==
运算符在继承树的根处为Unity可以引用的对象重载。以下是Unity手册中关于UnityEngine.Object
==
运算符的说明:
与null比较时要小心。
e.g。
GameObject go = new GameObject(); Debug.Log (go == null); // false Object obj = new Object(); Debug.Log (obj == null); // true
实例化GameObject会将其添加到场景中,因此它完全可以 初始化(!销毁)。实例化一个简单的UnityEngine.Object 没有这样的语义,所以( sic )它保留在&#39;被销毁的&#39;国家哪个 将true与null进行比较。
在实例化GameObject
初始化它时,实例化AudioSource
对象不会,因此与null
的比较会返回true
。
由于尝试引用未初始化的AudioSource
对象的属性会抛出空引用异常这一事实,这种不寻常的习惯变得更加隐蔽,我最初误解为对象引用是{{1不是财产。
其他人已经回答了OP的问题,但我想补充一下这个答案,因为如果其中的null
类不是我们的那个,那么OP的代码实际上可能有意义。我会期待它,就像Thread
(及其后代)并不是你在Unity脚本中所期望的那样(也就是说,它实际上是Object
,而不是UnityEngine.Object
,它会让您感到困惑的重载System.Object
运算符。)