2009-01-13 17 views
28

Hemos asumido algunos .NET 1.1 del código de servicio de Windows que genera hilos para leer mensajes de una cola (Ver más allá de la cola eGate JMS, pero eso no es importante) y genera hilos para procesar el mensaje en el servicio de aplicación de destino. Continuamente nos encontramos con decisiones de lógica y diseño que nos desconciertan sin fin. Aquí es un ejemplo, donde el mensaje (lsMessage) se ha recuperado de la cola y listo para el procesamiento¿Puede el constructor de objetos devolver un valor nulo?

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; 
    } 
} 

Por favor, ignora el uso de etiqueta y Goto por el momento; Esa no es la pregunta. Nuestro desconcierto es el comprobar si el objeto Thread es nulo justo después de la instanciación. La declaración else a continuación parece sugerir que los desarrolladores anteriores han encontrado situaciones como esta antes. Por supuesto, los desarrolladores originales desaparecieron hace mucho tiempo. Entonces, nos gustaría saber si el CLR puede crear una instancia de un objeto después de la llamada al constructor y devolver un valor nulo. No tenemos conocimiento de tal posibilidad.

+0

Parece el resultado de una refactorización o cambio de código para mí. Tal vez la línea de construcción era algo así como 'GetThread()'. – usr

Respuesta

29

En mi opinión, lo que sugiere la afirmación else es que los desarrolladores anteriores no conocían su C#. Un constructor siempre devuelve un objeto construido o lanza una excepción.

En los viejos tiempos, los constructores de C++ podían devolver null, así que tal vez el problema provenga de eso. Esto ya no es cierto en C++, al menos para el operador predeterminado new.

+1

Eso puede ser una posibilidad; los desarrolladores originales pueden tener algunos antecedentes C++. He visto programadores de C++ que habitualmente intentan hacer cosas "divertidas" en .NET .... – icelava

+2

Por cierto en Objective-C el constructor puede devolver nulo (nulo) – fnc12

+0

Intercambiando las dos primeras o haciendo que la segunda oración sea más prominente , haría que la respuesta sea más fácil de analizar (más directa) para que la usen personas que solo leyeron el "título de la pregunta", no toda la pregunta. –

25

Editar: una aclaración hay un caso borde loco donde se puede obtener null de un constructor de la clase, pero francamente no creo que ningún código real debería vez de esperar para hacer frente a este nivel de locura: What's the strangest corner case you've seen in C# or .NET? . Para todos intentos normales: no sucederá.


No, no se puede conseguir nula de una clase constructor (Thread es una clase). El único caso que conozco donde un constructor puede (parecen) volver null es Nullable<T> - es decir

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

Este es un problema un poco más grande con los genéricos:

static void Oops<T>() where T : new() { 
    T t = new T(); 
    if (t == null) throw new InvalidOperationException(); 
} 

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

(por supuesto, hay maneras de verificar/manejar ese escenario, como : class)

Aparte de eso, un constructor siempre devolverá un objeto (o inicializará una estructura), o emitirá una excepción.

+0

No usamos elementos obligatorios aquí, por supuesto; .NET 1.1 – icelava

+2

Cierto, pero pensé que valía la pena mencionarlo como un caso marginal ;-p –

+0

Y es * casi * siempre una referencia a un objeto recién creado, también. La única excepción a eso que yo sé es un caso de esquina de cuerda. –

2

¡NO! esa verificación nula es redundante. Muchos desarrolladores de C++ que se mudaron a C# tienen este hábito de un cheque nulo y supongo que aquí es lo mismo.

Lo único que debe hacer es verificar la documentación para ver si el constructor puede lanzar una excepción. En su caso, consulte http://msdn.microsoft.com/en-us/library/xx3ezzs2.aspx y, como se mencionó, el constructor siempre devolverá un obj válido.

+0

Esa parece ser la sospecha general: los hábitos de C++ ... – icelava

0

Como core menciona, la sobrecarga del operador puede hacer que parezca que un constructor devolvió null, cuando eso no es lo que realmente sucedió. Los autores del artículo core encontrado dicen que no lo han visto usar, pero en realidad se usa en un producto muy popular: la Unidad.

Esto compilar y registrar un mensaje en tiempo de ejecución:

using UnityEngine; 

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

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

Ahora, el problema aquí es mío, porque uno debe no llamada del constructor AudioSource directamente. Pero uno debe saber que el operador == está sobrecargado en la raíz del árbol de herencia para los objetos a los que Unity puede hacer referencia. Esto es lo que dice el manual de la Unidad sobre UnityEngine.Object 's == operador:

Tenga cuidado cuando se comparan con nula.

p. Ej.

GameObject go = new GameObject(); 
Debug.Log (go == null); // false 

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

Instatiating un GameObject añade a la escena de modo que esté completamente inicializado (! Destruidos). La instancia de un UnityEngine.Object simple no tiene esa semántica, por lo que (sic) permanece en el estado 'destruido' que compara verdadero con nulo.

Mientras instancias de un GameObject inicializa, crear instancias de un objeto AudioSource no lo hace, por lo que la comparación con los rendimientos nulltrue.

Este idioma inusual se hace aún más sigiloso por virtud del hecho de que los intentos para hacer referencia a propiedades de la AudioSource objeto no inicializado será lanzar excepciones null-referencia, que inicialmente I mal interpretado en el sentido de la referencia de objeto era null, no la propiedad.

Otros han respondido a la pregunta del OP, pero quería agregar esta respuesta porque el código OP podría tener sentido si la clase Thread allí no es la que esperaríamos, al igual que Object (y sus descendientes)) no es exactamente lo que cabría esperar en un script de Unity (es decir, en realidad es UnityEngine.Object, en lugar de System.Object, lo que le da el operador == sobrecargado que tanto me confundió).

Cuestiones relacionadas