[.net] Perché trovare l'inizializzatore di un tipo genera una NullReferenceException?


Answers

Poiché credo di aver trovato alcune nuove scoperte interessanti sul problema, ho deciso di aggiungerle come risposta, riconoscendo allo stesso tempo che non stanno affrontando il "perché succede" nella domanda originale. Forse qualcuno che conosce meglio il funzionamento interno dei tipi coinvolti potrebbe pubblicare una risposta edificante basata anche sulle osservazioni che sto postando.

Sono anche riuscito a riprodurre il problema sul mio computer e ho tracciato una connessione con l' interfaccia System.Runtime.InteropServices._Type , che è implementata dalla classe System.Type .

Inizialmente, ho trovato almeno 3 metodi alternativi per risolvere il problema:

  1. Semplicemente _Type il Type in _Type all'interno del metodo Main :

    var cctor = ((_Type)typeof(Test)).TypeInitializer;
    
  2. O assicurandosi che l'approccio 1 sia stato usato precedentemente all'interno del metodo:

    var warmUp = ((_Type)typeof(Test)).TypeInitializer; 
    var cctor = ((Type)typeof(Test)).TypeInitializer;
    
  3. O aggiungendo un campo statico alla classe Test e inizializzandolo (con il cast di _Type ):

    static ConstructorInfo _dummy1 = (typeof(object) as _Type).TypeInitializer;
    

Più tardi, ho scoperto che se non vogliamo coinvolgere l'interfaccia System.Runtime.InteropServices._Type nei workaround, il problema non si verifica né da:

  1. Aggiungere un campo statico alla classe Test e inizializzarlo (senza _Type a _Type ):

    static ConstructorInfo _dummy2 = typeof(object).TypeInitializer;
    
  2. Oppure inizializzando la variabile cctor stessa come un campo statico della classe:

    static ConstructorInfo cctor = typeof(Test).TypeInitializer;
    

Non vedo l'ora di ricevere il tuo feedback.

Question

Questo mi ha lasciato perplesso. Stavo cercando di ottimizzare alcuni test per Noda Time, dove abbiamo qualche tipo di controllo dell'inizializzatore. Ho pensato di scoprire se un tipo ha un inizializzatore di tipo (costruttore statico o variabili statiche con inizializzatori) prima di caricare tutto in un nuovo AppDomain . Con mia sorpresa, un piccolo test di questo ha gettato NullReferenceException - nonostante non ci fossero valori nulli nel mio codice. Solleva l'eccezione solo quando è compilata senza informazioni di debug.

Ecco un breve ma completo programma per dimostrare il problema:

using System;

class Test
{
    static Test() {}

    static void Main()
    {
        var cctor = typeof(Test).TypeInitializer;
        Console.WriteLine("Got initializer? {0}", cctor != null);
    }    
}

E una trascrizione di compilation e output:

c:\Users\Jon\Test>csc Test.cs
Microsoft (R) Visual C# Compiler version 4.0.30319.17626
for Microsoft (R) .NET Framework 4.5
Copyright (C) Microsoft Corporation. All rights reserved.


c:\Users\Jon\Test>test

Unhandled Exception: System.NullReferenceException: Object reference not set to
an instance of an object.
   at System.RuntimeType.GetConstructorImpl(BindingFlags bindingAttr, Binder bin
der, CallingConventions callConvention, Type[] types, ParameterModifier[] modifi
ers)
   at Test.Main()

c:\Users\Jon\Test>csc /debug+ Test.cs
Microsoft (R) Visual C# Compiler version 4.0.30319.17626
for Microsoft (R) .NET Framework 4.5
Copyright (C) Microsoft Corporation. All rights reserved.


c:\Users\Jon\Test>test
Got initializer? True

Ora noterai che sto usando .NET 4.5 (il candidato al rilascio) - che potrebbe essere rilevante qui. È piuttosto complicato per me testarlo con i vari altri framework originali (in particolare "vanilla" .NET 4) ma se qualcun altro ha un facile accesso alle macchine con altri framework, sarei interessato ai risultati.

Altri dettagli:

  • Sono su una macchina x64, ma questo problema si verifica con entrambi gli assembly x86 e x64
  • È la "debug-ness" del codice chiamante che fa la differenza - anche se nel caso di test sopra lo sta testando sul proprio assembly, quando ho provato questo contro Noda Time non ho dovuto ricompilare NodaTime.dll per vedere le differenze - solo Test.cs che si riferiva ad esso.
  • L'esecuzione dell'assemblaggio "rotto" su Mono 2.10.8 non gira

Qualche idea? Bug del framework?

EDIT: Curioso e curioso. Se si estrae la chiamata Console.WriteLine :

using System;

class Test
{
    static Test() {}

    static void Main()
    {
        var cctor = typeof(Test).TypeInitializer;
    }    
}

Ora fallisce solo quando compilato con csc /o- /debug- . Se attivi le ottimizzazioni, ( /o+ ) funziona. Ma se includi la chiamata a Console.WriteLine come nell'originale, entrambe le versioni falliranno.




Related