Obtention de tous les types dans un espace de noms via la réflexion


269

Comment obtenez-vous toutes les classes dans un espace de noms grâce à la réflexion en C #?


pouvez-vous modifier votre question ... la question du sous-texte est plus communicative que l '' espace de noms en C # '
Gishu

Vous pouvez regarder ici . Il existe 2 échantillons différents.
Fatih GÜRDAL

Réponses:


317

Le code suivant imprime les noms des classes spécifiées namespacedéfinies dans l'assembly actuel.
Comme d'autres gars l'ont souligné, un espace de noms peut être dispersé entre différents modules, vous devez donc d'abord obtenir une liste des assemblys.

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

83

Comme FlySwat le dit, vous pouvez avoir le même espace de noms couvrant plusieurs assemblages (par exemple System.Collections.Generic). Vous devrez charger tous ces assemblages s'ils ne sont pas déjà chargés. Donc pour une réponse complète:

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

Cela devrait fonctionner, sauf si vous voulez des classes d'autres domaines. Pour obtenir une liste de tous les domaines, suivez ce lien.


1
fonctionne bien - un petit rappel: j'ai essayé de supprimer " && t.Namespace == @namespace" - ce qui m'a donné tous les assemblages .net :-)
Netsi1964

@ Netsi1964 si vous supprimez, && t.Namespace == @namespacevous obtenez toutes les classes de tous les assemblys , y compris les .net. GetAssembliesvous donnera tous les assemblys et GetAssemblies().SelectMany(t => t.GetTypes())donnera tous les types (classes, structures, etc.) de tous les assemblys.
nawfal

J'ai mis à niveau vers DotNet Core 2.2 (à partir de 2.1) et ce code a cessé de fonctionner pour mon assemblage spécifique. L'assembly que je voulais n'était référencé nulle part dans le code et n'était donc pas chargé! En 2.1, il était chargé, mais 2.2 semble avoir un chargement paresseux?
Harvey

@Harvey .NET Core a-t-il un domaine d'application pour commencer?
nawfal

@nawfal Ouais. Ce code fonctionnait précédemment en 2.1. J'ai trouvé que je force le chargement d'un assemblage en utilisant très bien Assembly.Load(nameof(NameOfMyNamespace))travaillé.
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: Le code ci-dessus illustre ce qui se passe. Si vous l'implémentiez, une version simplifiée peut être utilisée:

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

9
Je n'essaie pas d'être méchant, mais il y a une liste et une itération entièrement inutiles à travers tous les éléments trouvés dans ce code; la variable "classlist" et foreach via "namespacelist" ne fournissent aucune fonctionnalité différente du retour de "namespacelist"
TheXenocide

10
@TheXenocide l'objectif d'un exemple de code n'est pas toujours destiné à montrer la "meilleure" façon d'écrire du code, mais à expliquer clairement comment quelque chose est fait.
Ryan Farley

4
Je le faisais simplement remarquer pour le bien de l'éducation; c'est notre responsabilité de faire en sorte que les gens apprennent du meilleur exemple possible plutôt que de risquer un mauvais exemple qui influence négativement la compréhension. Je ne dis pas que cela en particulier est préjudiciable, mais je ne suis pas d'accord avec le sentiment
TheXenocide

4
Je vote contre une réponse si elle n'est pas utile à la question qui a été posée. L'astuce que vous voyez lorsque vous survolez le bouton de vote haut / bas indique "Cela a été utile". La décision de voter pour / contre une réponse, pour moi, est de savoir si cela a été utile ou non pour répondre à la question posée.
Ryan Farley

3
La seule chose que vous avez utilisée en utilisant deux listes et deux itérations a aidé à me ralentir en essayant de comprendre pourquoi vous avez utilisé deux listes et ne vous êtes pas contenté d'ajouter directement à classlistla première itération du asm.GetTypes()résultat.
ProfK

20

Pour un assembly, un espace de nom et un nom de classe spécifiques:

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

Remarque: Le projet doit référencer l'assemblage


12

Voici un correctif pour les erreurs LoaderException que vous êtes susceptible de trouver si l'un des types sous-classe un type dans un autre assembly:

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

Cela devrait aider à charger les types définis dans d'autres assemblys.

J'espère que cela pourra aider!


Bien sûr, cela semble utile, et moins plus utile et moins déroutant que le code de Ryan Farley, même sans y penser.
ProfK

Mais vous m'avez aussi confus pendant un certain temps. Je ne peux que deviner que la Assembly asubstance représente le traitement normal qui pourrait provoquer le déclenchement de cet événement. Je ne vois aucune utilité pour aaider à LoaderExceptionrésoudre les erreurs. Ai-je raison?
ProfK

9

Vous ne pourrez pas obtenir tous les types dans un espace de noms, car un espace de noms peut relier plusieurs assemblys, mais vous pouvez obtenir toutes les classes dans un assemblage et vérifier si elles appartiennent à cet espace de noms.

Assembly.GetTypes()fonctionne sur l'assembly local, ou vous pouvez d'abord charger un assembly puis l'appeler GetTypes().


2
+1 pour la bonne réponse. AppDomain.CurrentDomain.GetAssembliespeut être utile.
nawfal

... puis parcourez-les, en filtrant ceux qui ne correspondent pas à l'espace de noms.
TJ Crowder

OP a spécifiquement demandé des "classes dans un espace de noms", alors que cela vous permet de "taper dans un assembly" - donc cette réponse est incomplète. La bonne réponse est probablement celle-ci , qui énumère uniquement les classes, de tous les assemblys.
mindplay.dk

6

Tout comme la réponse @aku, mais en utilisant des méthodes d'extension:

string @namespace = "...";

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

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

5

Obtenez toutes les classes par partie du nom de l'espace de noms sur une seule ligne:

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

3

Les espaces de noms sont en fait plutôt passifs dans la conception du runtime et servent principalement d'outils d'organisation. Le nom complet d'un type dans .NET se compose de l'espace de noms et de Class / Enum / Etc. combiné. Si vous souhaitez uniquement passer par un assembly spécifique, vous devez simplement parcourir les types renvoyés par assembly. GetExportedTypes () vérifiant la valeur de type. Espace de noms . Si vous tentiez de parcourir tous les assemblys chargés dans l'AppDomain actuel, cela impliquerait d'utiliser AppDomain.CurrentDomain. GetAssemblies ()


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 

Quel est AttributeClassle nom MustHaveAttributes? Je ne vois rien concernant le test si une classe a des attributs ou non. C'est plus déroutant qu'utile.
ProfK

1

Assez facile

Type[] types = Assembly.Load(new AssemblyName("mynamespace.folder")).GetTypes();
foreach (var item in types)
{
}

Et tout simplement ne pas répondre à la question. Tout cela consiste à obtenir une liste de tous les types dans un seul assembly, indépendamment de tout espace de noms particulier.
Ian
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.