2010-09-21 13 views
17

Me he encontrado con un comportamiento extraño en mi (enorme) proyecto .NET 4. En algún momento en el código, me refiero a un tipo completo, por ejemplo:¿Por qué System.Type.GetType ("Xyz") devuelve null si typeof (Xyz) existe?

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

más adelante, hago esto:

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

y regrese null. No puedo entender por qué sucede esto, porque mi nombre de tipo es correcto y lo he comprobado con otros tipos y se resuelven correctamente. Por otra parte, la siguiente consulta LINQ encuentra el 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(); 

¿Hay alguna razón por la System.Type.GetType podría fallar?

finalmente he tenido que recurrir a este pedazo de código en lugar de 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; 
} 

Respuesta

26

Si acaba de dar un nombre de clase (que qué tienen que ser completamente calificado en términos de espacio de nombres, por supuesto) Type.GetType(string) solamente se verá en la asamblea que se está ejecutando y mscorlib. Si desea obtener tipos de cualquier otro ensamblaje, debe especificar el nombre completo completo, incluida la información de ensamblaje. Como dice François, Type.AssemblyQualifiedName es una buena forma de ver esto. He aquí un ejemplo:

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

Salida:

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

Tenga en cuenta que si utiliza un ensamblado con nombre (como Form en este caso) debe incluir toda la información de ensamblado - versiones, símbolo de clave pública, etc.

Si está utilizando un conjunto no-con nombre seguro, es más fácil - algo así como:

Foo.Bar.Baz, MyCompany.MyAssembly 

para un tipo de llamada en Baz espacio de nombres Foo.Bar, en el ensamblaje MyCompany.MyAssembly. Tenga en cuenta la ausencia de ".dll" al final: es parte del nombre del archivo, pero no del nombre del ensamblado.

También debe tener en cuenta las diferencias entre los nombres C# y los nombres CLR para cosas como clases anidadas y genéricos. Por ejemplo, typeof(List<>.Enumerator) tiene un nombre de System.Collections.Generic.List`1+Enumerator[T]. El lado de los genéricos es difícil de resolver, pero el bit de tipo anidado es fácil, solo se representa con un "+" en lugar del "." lo usarías en C#.

+0

Muchas gracias por su respuesta. De hecho, todos los demás tipos que estaba resolviendo hasta ahora estaban ubicados en el mismo ensamblado o en mscorlib, por lo que no detecté el error antes. –

+0

Proporcionar 'System.Type.GetType' con información de ensamblaje parcial funciona incluso si el ensamblado tiene un nombre fuerte. He comprobado 'System.Type.GetType (" Foo.Bar.Baz, MyCompany.MyAssembly ")' y funciona incluso si 'MyCompany.Assembly' tiene un nombre seguro. –

+0

para futuras referencias, si desea usar el backtick en un código resaltado (y USTED probablemente lo hará;), use doble backticks para comenzar y cerrar la cita :). Ver [aquí] (http://meta.stackexchange.com/q/82718/237379). – Noctis

4

Por lo que yo sé GetType busca "XYZ" en una asamblea llamada Foo.Bar.dll y estoy asumiendo que no existe.

GetType se basa en pasar la ruta exacta a Xyz en el conjunto. El ensamblaje y el espacio de nombre no tienen que estar relacionados.

Pruebe System.Type type = System.Type.GetType(typeof(Foo.Bar.Xyz).AssemblyQualifiedName) y vea si eso funciona.

El motivo por el que lo encuentra con su ejemplo de LINQ es que está utilizando GetAssemblies que obtiene los ensamblajes que se han cargado en el contexto de ejecución actual y tiene los detalles necesarios para encontrar todos los tipos dentro de los ensamblajes.

4

Desde el MSDN documentation (el subrayado es mío):

Si typeName incluye el espacio de nombres, pero no el nombre de ensamblado, este método busca en sólo el montaje del objeto que llama y Mscorlib.dll, en ese orden. Si typeName está totalmente calificado con el nombre del ensamblaje parcial o completo, este método busca en el ensamblaje especificado. Si el ensamblaje tiene un nombre fuerte, se requiere un nombre de ensamblaje completo.

1

Me tropecé con un problema similar y quiere dejar esto aquí

En primer lugar su puede especificar el AssemblyName en la cadena

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

Sin embargo esto sólo funciona para las asambleas sin un fuerte nombre. La explicación ya está en la respuesta de Simons If the assembly has a strong name, a complete assembly name is required.

Mi problema fue que tuve que resolver un System.Dictionary<?,?> de una cadena en tiempo de ejecución. Para un Dictionary<int, string>, esto podría ser fácil, pero ¿qué tal un Dictionary<int, Image>?

esto resultaría en

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

Pero no quiero escribir el nombre seguro. Especialmente porque no quiero incluir las versiones ya que planeo enfocar múltiples marcos con mi código.

Así que aquí está mi solución

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(...) tiene una sobrecarga, que acceps una func para el montaje y escriba la resolución que a su ordenada. Assembly.LoadWithPartialName está en desuso, pero si se descarta en el futuro podría pensar en un reemplazo (iterar todos los ensamblados en el dominio de aplicación actual y comparar los nombres parciales).

Cuestiones relacionadas