2010-09-21 25 views
17

Mi sono imbattuto in uno strano comportamento nel mio (enorme) progetto .NET 4. Ad un certo punto del codice, mi riferisco a un tipo completo, dire:Perché System.Type.GetType ("Xyz") restituisce null se typeof (Xyz) esiste?

System.Type type = typeof (Foo.Bar.Xyz); 

più tardi, faccio questo:

System.Type type = System.Type.GetType ("Foo.Bar.Xyz"); 

e torno null. Non riesco a capire perché questo sta accadendo, perché il mio nome tipo è corretto, e ho controllato con altri tipi e vengono risolti correttamente. Inoltre, la seguente query LINQ trova il tipo:

var types = from assembly in System.AppDomain.CurrentDomain.GetAssemblies() 
      from assemblyType in assembly.GetTypes() 
      where assemblyType.FullName == typeName 
      select assemblyType; 

System.Type type = types.FirstOrDefault(); 

Esistono motivi per cui System.Type.GetType potrebbe fallire?

Ho finalmente dovuto ricorrere a questo pezzo di codice, invece di GetType:

System.Type MyGetType(string typeName) 
{ 
    System.Type type = System.Type.GetType (typeName); 

    if (type == null) 
    { 
     var types = from assembly in System.AppDomain.CurrentDomain.GetAssemblies() 
        from assemblyType in assembly.GetTypes() 
        where assemblyType.FullName == typeName 
        select assemblyType; 

     type = types.FirstOrDefault(); 
    } 

    return type; 
} 

risposta

26

Se basta dare un nome di classe (che fa bisogno di essere pienamente qualificati in termini di spazio dei nomi, ovviamente) Type.GetType(string) guarderà solo nel montaggio attualmente in esecuzione e mscorlib. Se si desidera ottenere tipi da qualsiasi altro assieme, è necessario specificare il nome completo completo, comprese le informazioni sull'assieme. Come dice François, Type.AssemblyQualifiedName è un buon modo di vederlo. Ecco un esempio:

using System; 
using System.Windows.Forms; 

class Test 
{ 
    static void Main() 
    { 
     string name = typeof(Form).AssemblyQualifiedName; 
     Console.WriteLine(name); 

     Type type = Type.GetType(name); 
     Console.WriteLine(type); 
    } 
} 

uscita:

System.Windows.Forms.Form, System.Windows.Forms, Version = 4.0.0.0, Culture = neutral, PublicKeyToken =
b77a5c561934e089
System.Windows.Forms.Form

si noti che se si sta utilizzando un assembly di nome fortemente (come Form in questo caso) è necessario includere tutte le informazioni di montaggio - il controllo delle versioni, token di chiave pubblica ecc

Se si utilizza un assembly non fortemente nome, è più facile - qualcosa come:

Foo.Bar.Baz, MyCompany.MyAssembly 

per un tipo chiamato Baz in namespace Foo.Bar, nell'assemblaggio MyCompany.MyAssembly. Notare l'assenza di ".dll" alla fine, che è parte del nome del file, ma non il nome dell'assembly.

Si dovrebbe anche essere consapevoli delle differenze tra nomi C# e nomi CLR per cose come classi annidate e generici. Ad esempio, typeof(List<>.Enumerator) ha un nome di System.Collections.Generic.List`1+Enumerator[T]. Il lato generico è complicato da risolvere, ma il bit del tipo annidato è semplice: è semplicemente rappresentato con un "+" anziché il "." useresti in C#.

+0

Grazie mille per la risposta. In effetti, tutti gli altri tipi che stavo risolvendo fino ad ora si trovavano nello stesso assembly o in mscorlib, quindi non ho catturato l'errore prima. –

+0

Fornire 'System.Type.GetType' con informazioni di assemblaggio parziali funziona anche se l'assembly ha un nome sicuro. Ho controllato 'System.Type.GetType (" Foo.Bar.Baz, MyCompany.MyAssembly ")' e funziona anche se 'MyCompany.Assembly' ha un nome forte. –

+0

per riferimento futuro, se si desidera utilizzare il backtick in un evidenziatore del codice (e probabilmente lo si farà;), utilizzare double backtick per iniziare e chiudere la citazione :). Vedi [qui] (http://meta.stackexchange.com/q/82718/237379). – Noctis

4

Per quanto ne so GetType cerca "XYZ" in un assembly di nome Foo.Bar.dll e sto supponendo che non esista.

GetType fa affidamento sul passaggio del percorso esatto a Xyz nell'assieme. L'assembly e lo spazio dei nomi non devono essere correlati.

Provare System.Type type = System.Type.GetType(typeof(Foo.Bar.Xyz).AssemblyQualifiedName) e vedere se funziona.

Il motivo per cui lo si trova con il proprio esempio LINQ è che si sta utilizzando GetAssemblies che ottiene gli assembly caricati nel contesto di esecuzione corrente e quindi ha i dettagli necessari per trovare tutti i tipi all'interno degli assiemi.

4

Dal MSDN documentation (il corsivo è mio):

Se typeName include lo spazio dei nomi, ma non il nome di montaggio, questo metodo ricerche solo l'assemblaggio chiamando dell'oggetto e Mscorlib.dll, in questo ordine. Se typeName è completamente qualificato con il nome dell'assembly parziale o completo, questo metodo esegue la ricerca nell'assembly specificato. Se l'assembly ha un nome sicuro, è necessario un nome assembly completo.

1

Ho appena incappato in un problema simile e di volere lasciare questo qui

Prima di tutto il vostro grado di specificare l'AssemblyName nella stringa

var type = System.Type.GetType("Foo.Bar.Xyz, Assembly.Name"); 

Tuttavia questo funziona solo per le assemblee senza una forte nome. La spiegazione è già in risposta Simons If the assembly has a strong name, a complete assembly name is required.

Il mio problema era che dovevo risolvere uno System.Dictionary<?,?> da una stringa in fase di esecuzione. Per un Dictionary<int, string> questo potrebbe essere facile ma che dire di un Dictionary<int, Image>?

questo si tradurrebbe in

var typeName = "System.Collections.Generic.Dictionary`2[System.Int32, [System.Drawing.Image, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a]]"; 

Ma io non voglio scrivere il nome forte. Soprattutto perché non voglio includere le versioni poiché sto progettando di indirizzare più framework con il mio codice.

Così qui è la mia soluzione

privat statice void Main() 
    { 
     var typeName = "System.Collections.Generic.Dictionary`2[System.Int32, [System.Drawing.Image, System.Drawing]]"; 
     var type = Type.GetType(typeName, ResolveAssembly, ResolveType); 
    } 

    private static Assembly ResolveAssembly(AssemblyName assemblyName) 
    { 
     if (assemblyName.Name.Equals(assemblyName.FullName)) 
      return Assembly.LoadWithPartialName(assemblyName.Name); 
     return Assembly.Load(assemblyName); 
    } 

    private static Type ResolveType(Assembly assembly, string typeName, bool ignoreCase) 
    { 
     return assembly != null 
      ? assembly.GetType(typeName, false, ignoreCase) 
      : Type.GetType(typeName, false, ignoreCase); 
    } 

Type.GetType(...) ha un sovraccarico che acceps un func per l'assemblaggio e digitare la risoluzione che, in ordinata. Assembly.LoadWithPartialName è deprecato ma se verrà eliminato in futuro potrei pensare a una sostituzione (iterare tutti gli assembly nell'AppDomain corrente e confrontare i nomi parziali).

Problemi correlati