Abrufen aller Typen in einem Namespace durch Reflektion

268

Wie erhalten Sie alle Klassen in einem Namespace durch Reflektion in C #?

HB
quelle
Können Sie Ihre Frage bearbeiten? Die Subtext-Frage ist kommunikativer als der 'Namespace in C #'
Gishu
Sie können hier schauen . Es gibt 2 verschiedene Beispiele.
Fatih GÜRDAL

Antworten:

316

Der folgende Code druckt Namen von Klassen in der angegebenen namespaceaktuellen Assembly.
Wie andere Leute betonten, kann ein Namespace auf verschiedene Module verteilt sein, daher müssen Sie zuerst eine Liste der Assemblys abrufen.

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
quelle
83

Wie FlySwat sagt, können Sie denselben Namespace in mehreren Assemblys (z System.Collections.Generic. B. ) verwenden. Sie müssen alle diese Assemblys laden, wenn sie noch nicht geladen sind. Für eine vollständige Antwort:

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

Dies sollte funktionieren, es sei denn, Sie möchten Klassen anderer Domänen. Folgen Sie diesem Link, um eine Liste aller Domains zu erhalten .

nawfal
quelle
1
funktioniert gut - eine kleine Erinnerung: Ich habe versucht, " && t.Namespace == @namespace" zu entfernen - was mir natürlich alle .net-Assemblys gab :-)
Netsi1964
@ Netsi1964 , wenn Sie entfernen && t.Namespace == @namespaceSie alle bekommen Klassen von allen Baugruppen , .net ist inklusive. GetAssembliesgibt Ihnen alle Assemblys und GetAssemblies().SelectMany(t => t.GetTypes())gibt alle Typen (Klassen, Strukturen usw.) aus allen Assemblys an.
Nawfal
Ich habe ein Upgrade auf DotNet Core 2.2 (von 2.1) durchgeführt und dieser Code funktioniert für meine spezifische Assembly nicht mehr. Die Assembly, die ich wollte, wurde nirgendwo im Code referenziert und wurde daher nicht geladen! In 2.1 wurde es geladen, aber 2.2 scheint faul zu laden?
Harvey
@ Harvey Hat .NET Core zunächst eine Appdomain?
Nawfal
@nawfal Ja. Dieser Code funktionierte zuvor in 2.1. Ich fand, dass ich das Laden einer Baugruppe erzwinge, indem ich gut Assembly.Load(nameof(NameOfMyNamespace))funktioniere.
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: Der obige Code zeigt, was los ist. Wenn Sie es implementieren, kann eine vereinfachte Version verwendet werden:

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
quelle
9
Ich versuche nicht gemein zu sein, aber es gibt eine völlig unnötige Liste und Iteration durch alle gefundenen Elemente in diesem Code; Die Variable "classlist" und foreach through "namespacelist" bieten keine andere Funktionalität als die Rückgabe von "namespacelist"
TheXenocide
10
@TheXenocide Der Zweck eines Codebeispiels ist nicht immer dazu gedacht, den "besten" Weg zum Schreiben von Code aufzuzeigen, sondern klar zu vermitteln, wie etwas getan wird.
Ryan Farley
4
Ich habe nur aus Gründen der Bildung darauf hingewiesen; Es liegt in unserer Verantwortung, die materiellen Menschen aus dem besten Beispiel zu lernen, das wir können, anstatt ein schlechtes Beispiel zu riskieren, das das Verständnis negativ beeinflusst. Ich sage nicht, dass dies besonders schädlich ist, aber ich bin nicht einverstanden mit dem Gefühl
TheXenocide
4
Ich stimme eine Antwort ab, wenn sie für die gestellte Frage nicht hilfreich ist. Der Hinweis, den Sie sehen, wenn Sie mit der Maus über die Schaltfläche zum Abstimmen nach oben / unten fahren, lautet "Dies war hilfreich". Die Entscheidung, eine Antwort nach oben / unten abzustimmen, ist für mich, ob sie bei der Beantwortung der gestellten Frage hilfreich war oder nicht.
Ryan Farley
3
Das einzige, was Ihnen mit zwei Listen und zwei Iterationen geholfen hat, war, mich zu verlangsamen und herauszufinden, warum Sie zwei Listen verwendet haben und nicht nur classlistbei der ersten Iteration über das asm.GetTypes()Ergebnis direkt hinzugefügt haben .
ProfK
20

Für eine bestimmte Assembly, NameSpace und 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();

Hinweis: Das Projekt muss auf die Baugruppe verweisen

John Peters
quelle
12

Hier ist eine Korrektur für LoaderException-Fehler, die Sie wahrscheinlich finden, wenn einer der Typen einen Typ in einer anderen Assembly unterverteilt:

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

Dies sollte beim Laden von Typen helfen, die in anderen Baugruppen definiert sind.

Ich hoffe, das hilft!

Tsimon
quelle
Sicher sieht es hilfreich aus und ist weniger hilfreich und weniger verwirrend als der Code von Ryan Farley, auch ohne darüber nachzudenken.
ProfK
Du hast mich aber auch eine Weile verwirrt. Ich kann immer noch nur vermuten, dass das Assembly aZeug die normale Verarbeitung darstellt, die dieses Ereignis auslösen könnte. Ich sehe keinen aSinn darin, bei LoaderExceptionFehlern zu helfen . Habe ich recht?
ProfK
9

Sie können nicht alle Typen in einem Namespace abrufen, da ein Namespace mehrere Assemblys überbrücken kann. Sie können jedoch alle Klassen in einer Assembly abrufen und prüfen, ob sie zu diesem Namespace gehören.

Assembly.GetTypes()funktioniert auf der lokalen Assembly, oder Sie können zuerst eine Assembly laden und dann aufrufen GetTypes().

FlySwat
quelle
2
+1 für die richtige Antwort. AppDomain.CurrentDomain.GetAssemblieskann hilfreich sein.
Nawfal
... und durchlaufen sie dann und filtern diejenigen heraus, die nicht mit dem Namespace übereinstimmen.
TJ Crowder
OP hat speziell nach "Klassen in einem Namespace" gefragt, während Sie dadurch "eine Assembly eingeben" - diese Antwort ist also unvollständig. Die richtige Antwort ist wahrscheinlich diese , die nur Klassen aus allen Assemblys auflistet.
mindplay.dk
6

Genau wie @aku Antwort, aber mit Erweiterungsmethoden:

string @namespace = "...";

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

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

Holen Sie sich alle Klassen nach einem Teil des Namespace-Namens in nur einer Zeile:

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

Namespaces sind im Design der Laufzeit eigentlich eher passiv und dienen in erster Linie als organisatorische Werkzeuge. Der vollständige Name eines Typs in .NET besteht aus dem Namespace und Class / Enum / Etc. kombiniert. Wenn Sie nur eine bestimmte Baugruppe durchlaufen möchten, durchlaufen Sie einfach die von der Baugruppe zurückgegebenen Typen. GetExportedTypes () überprüft den Wert des Typs. Namespace . Wenn Sie versuchen würden, alle in der aktuellen AppDomain geladenen Assemblys durchzugehen, müssten Sie AppDomain.CurrentDomain verwenden. GetAssemblies ()

TheXenocide
quelle
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
quelle
Worum geht es bei AttributeClassdem Namen MustHaveAttributes? Ich sehe nichts in Bezug auf das Testen, ob eine Klasse Attribute hat oder nicht. Das ist eher verwirrend als hilfreich.
ProfK
1

Ziemlich einfach

Type[] types = Assembly.Load(new AssemblyName("mynamespace.folder")).GetTypes();
foreach (var item in types)
{
}
Antonio Lopes
quelle
Und ganz einfach die Frage nicht beantworten. Dazu wird lediglich eine Liste aller Typen in einer einzelnen Assembly abgerufen, unabhängig von einem bestimmten Namespace.
Ian