Obtener todos los tipos en un espacio de nombres mediante reflexión

269

¿Cómo se obtienen todas las clases en un espacio de nombres a través de la reflexión en C #?

media pensión
fuente
puedes editar tu pregunta ... la pregunta del subtexto es más comunicativa que el 'Espacio de nombres en C #'
Gishu
Puedes mirar aquí . Hay 2 muestras diferentes.
Fatih GÜRDAL

Respuestas:

317

El siguiente código imprime los nombres de las clases especificadas namespaceen el ensamblaje actual.
Como otros muchachos señalaron, un espacio de nombres puede estar disperso entre diferentes módulos, por lo que primero debe obtener una lista de ensamblajes.

string nspace = "...";

var q = from t in Assembly.GetExecutingAssembly().GetTypes()
        where t.IsClass && t.Namespace == nspace
        select t;
q.ToList().ForEach(t => Console.WriteLine(t.Name));
aku
fuente
83

Como dice FlySwat, puede tener el mismo espacio de nombres que abarca varios ensamblajes (por ejemplo, por ejemplo System.Collections.Generic). Tendrá que cargar todos esos ensamblajes si aún no están cargados. Entonces, para una respuesta completa:

AppDomain.CurrentDomain.GetAssemblies()
                       .SelectMany(t => t.GetTypes())
                       .Where(t => t.IsClass && t.Namespace == @namespace)

Esto debería funcionar a menos que desee clases de otros dominios. Para obtener una lista de todos los dominios, siga este enlace.

nawfal
fuente
1
funciona bien, un pequeño recordatorio: intenté eliminar " && t.Namespace == @namespace", lo que me dio todos los ensamblados .net :-)
Netsi1964
@ Netsi1964 si elimina && t.Namespace == @namespace, obtiene todas las clases de todos los ensamblados , incluidos .net. GetAssembliesle dará todos los ensamblajes y GetAssemblies().SelectMany(t => t.GetTypes())todos los tipos (clases, estructuras, etc.) de todos los ensamblajes.
nawfal
Actualicé a DotNet Core 2.2 (desde 2.1) y este código dejó de funcionar para mi ensamblaje específico. ¡El ensamblaje que quería no estaba referenciado en ninguna parte del código, por lo que no se cargó! En 2.1 se cargó, pero 2.2 parece tener carga lenta?
Harvey
@ Harvey ¿Tiene .NET Core un dominio de aplicación para empezar?
nawfal
@nawfal Sí. Este código funcionó anteriormente en 2.1. Descubrí que fuerzo la carga de un ensamblaje utilizando Assembly.Load(nameof(NameOfMyNamespace))trabajado bien.
Harvey
28
using System.Reflection;
using System.Collections.Generic;
//...

static List<string> GetClasses(string nameSpace)
{
    Assembly asm = Assembly.GetExecutingAssembly();

    List<string> namespacelist = new List<string>();
    List<string> classlist = new List<string>();

    foreach (Type type in asm.GetTypes())
    {
        if (type.Namespace == nameSpace)
            namespacelist.Add(type.Name);
    }

    foreach (string classname in namespacelist)
        classlist.Add(classname);

    return classlist;
}

NB: el código anterior ilustra lo que está sucediendo. Si lo implementara, se puede utilizar una versión simplificada:

using System.Linq;
using System.Reflection;
using System.Collections.Generic;
//...

static IEnumerable<string> GetClasses(string nameSpace)
{
    Assembly asm = Assembly.GetExecutingAssembly();
    return asm.GetTypes()
        .Where(type => type.Namespace == nameSpace)
        .Select(type => type.Name);
}
Ryan Farley
fuente
9
No estoy tratando de ser malo, pero hay una lista e iteración completamente innecesarias a través de todos los elementos encontrados en este código; la variable "classlist" y foreach mediante "namespacelist" no proporcionan una funcionalidad diferente a la de "namespacelist"
TheXenocide
10
@TheXenocide el propósito de una muestra de código no siempre pretende mostrar la "mejor" forma de escribir código, sino transmitir claramente cómo se hace algo.
Ryan Farley
44
Solo lo señalaba por el bien de la educación; es nuestra responsabilidad hacer que las personas materiales aprendan del mejor ejemplo que podamos en lugar de arriesgarnos a un mal ejemplo que influya negativamente en la comprensión. No digo que esto en particular sea perjudicial, pero no estoy de acuerdo con el sentimiento
TheXenocide
44
Voto abajo una respuesta si no es útil para la pregunta que se hizo. La sugerencia que ve al pasar el mouse sobre el botón de votación arriba / abajo dice "Esto fue útil". La decisión de votar arriba / abajo de una respuesta, para mí, es si fue útil o no para responder la pregunta formulada.
Ryan Farley
3
Lo único que me ayudó a usar dos listas y dos iteraciones fue retrasarme tratando de averiguar por qué usaste dos listas y no solo agregaste directamente a classlistla primera iteración sobre el asm.GetTypes()resultado.
ProfK
20

Para un ensamblado específico, NameSpace y ClassName:

var assemblyName = "Some.Assembly.Name"
var nameSpace = "Some.Namespace.Name";
var className = "ClassNameFilter";

var asm = Assembly.Load(assemblyName);
var classes = asm.GetTypes().Where(p =>
     p.Namespace == nameSpace &&
     p.Name.Contains(className) 
).ToList();

Nota: El proyecto debe hacer referencia al ensamblaje.

John Peters
fuente
12

Aquí hay una solución para los errores de LoaderException que es probable que encuentre si uno de los tipos subclasifica un tipo en otro ensamblado:

// Setup event handler to resolve assemblies
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += new ResolveEventHandler(CurrentDomain_ReflectionOnlyAssemblyResolve);

Assembly a = System.Reflection.Assembly.ReflectionOnlyLoadFrom(filename);
a.GetTypes();
// process types here

// method later in the class:
static Assembly CurrentDomain_ReflectionOnlyAssemblyResolve(object sender, ResolveEventArgs args)
{
    return System.Reflection.Assembly.ReflectionOnlyLoad(args.Name);
}

Eso debería ayudar con los tipos de carga definidos en otros ensamblajes.

¡Espero que ayude!

tsimon
fuente
Claro que parece útil, y menos útil y menos confuso que el código de Ryan Farley, incluso sin pensarlo.
ProfK
Sin embargo, también me confundiste por un tiempo. Todavía solo puedo adivinar que el Assembly amaterial representa el procesamiento normal que podría provocar que este evento se active. No veo ninguna utilidad para aayudar con los LoaderExceptionerrores. Estoy en lo cierto?
ProfK
9

No podrá obtener todos los tipos en un espacio de nombres, porque un espacio de nombres puede unir múltiples ensamblajes, pero puede obtener todas las clases en un ensamblaje y verificar si pertenecen a ese espacio de nombres.

Assembly.GetTypes()funciona en el ensamblaje local, o puede cargar un ensamblaje primero y luego llamarlo GetTypes().

FlySwat
fuente
2
+1 para la respuesta correcta. AppDomain.CurrentDomain.GetAssembliespuede ser útil
nawfal
... y luego recorrerlos, filtrando los que no coinciden con el espacio de nombres.
TJ Crowder
OP solicitó específicamente "clases en un espacio de nombres", mientras que esto le da "tipos en un ensamblado", por lo que esta respuesta es incompleta. La respuesta correcta es probablemente esta , que enumera solo las clases, de todos los ensamblados.
mindplay.dk
6

Al igual que @aku answer, pero usando métodos de extensión:

string @namespace = "...";

var types = Assembly.GetExecutingAssembly().GetTypes()
    .Where(t => t.IsClass && t.Namespace == @namespace)
    .ToList();

types.ForEach(t => Console.WriteLine(t.Name));
JoanComasFdz
fuente
5

Obtenga todas las clases por parte del nombre del espacio de nombres en una sola fila:

var allClasses = Assembly.GetExecutingAssembly().GetTypes().Where(a => a.IsClass && a.Namespace != null && a.Namespace.Contains(@"..your namespace...")).ToList();
Ivo Stoyanov
fuente
3

Los espacios de nombres son bastante pasivos en el diseño del tiempo de ejecución y sirven principalmente como herramientas organizativas. El nombre completo de un tipo en .NET consiste en el espacio de nombres y Clase / Enum / Etc. conjunto. Si solo desea pasar por un ensamblaje específico, simplemente recorrerá los tipos devueltos por el ensamblaje. GetExportedTypes () comprobando el valor de tipo. Espacio de nombres . Si intentara pasar por todos los ensamblados cargados en el AppDomain actual, implicaría usar AppDomain.CurrentDomain. GetAssemblies ()

TheXenocide
fuente
2
//a simple combined code snippet 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace MustHaveAttributes
{
  class Program
  {
    static void Main ( string[] args )
    {
      Console.WriteLine ( " START " );

      // what is in the assembly
      Assembly a = Assembly.Load ( "MustHaveAttributes" );
      Type[] types = a.GetTypes ();
      foreach (Type t in types)
      {

        Console.WriteLine ( "Type is {0}", t );
      }
      Console.WriteLine (
         "{0} types found", types.Length );

      #region Linq
      //#region Action


      //string @namespace = "MustHaveAttributes";

      //var q = from t in Assembly.GetExecutingAssembly ().GetTypes ()
      //        where t.IsClass && t.Namespace == @namespace
      //        select t;
      //q.ToList ().ForEach ( t => Console.WriteLine ( t.Name ) );


      //#endregion Action  
      #endregion

      Console.ReadLine ();
      Console.WriteLine ( " HIT A KEY TO EXIT " );
      Console.WriteLine ( " END " );
    }
  } //eof Program


  class ClassOne
  {

  } //eof class 

  class ClassTwo
  {

  } //eof class


  [System.AttributeUsage ( System.AttributeTargets.Class |
    System.AttributeTargets.Struct, AllowMultiple = true )]
  public class AttributeClass : System.Attribute
  {

    public string MustHaveDescription { get; set; }
    public string MusHaveVersion { get; set; }


    public AttributeClass ( string mustHaveDescription, string mustHaveVersion )
    {
      MustHaveDescription = mustHaveDescription;
      MusHaveVersion = mustHaveVersion;
    }

  } //eof class 

} //eof namespace 
Yordan Georgiev
fuente
¿De qué se trata AttributeClassel nombre MustHaveAttributes? No veo nada relacionado con probar si una clase tiene atributos o no. Esto es más confuso que útil.
ProfK
1

Bastante sencillo

Type[] types = Assembly.Load(new AssemblyName("mynamespace.folder")).GetTypes();
foreach (var item in types)
{
}
Antonio Lopes
fuente
Y simplemente no responde la pregunta. Todo lo que está haciendo es obtener una lista de todos los tipos en un solo ensamblaje, independientemente de cualquier espacio de nombres en particular.
Ian