Uzyskiwanie wszystkich typów w przestrzeni nazw poprzez odbicie


268

Jak uzyskać wszystkie klasy w przestrzeni nazw poprzez odbicie w C #?


czy możesz edytować swoje pytanie ... pytanie podtekstowe jest bardziej komunikatywne niż „Przestrzeń nazw w C #”
— Gishu

Możesz zajrzeć tutaj . Istnieją 2 różne próbki.
— Fatih GÜRDAL

Odpowiedzi:


316

Poniższy kod wyświetla nazwy klas w określonym namespacezdefiniowanym w bieżącym zestawie.
Jak zauważyli inni faceci, przestrzeń nazw może być rozproszona między różnymi modułami, więc najpierw musisz uzyskać listę zestawów.

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

Jak mówi FlySwat, możesz mieć tę samą przestrzeń nazw obejmującą wiele zestawów (na przykład System.Collections.Generic). Będziesz musiał załadować wszystkie te zespoły, jeśli jeszcze nie zostały załadowane. Aby uzyskać pełną odpowiedź:

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

Powinno to działać, chyba że chcesz klas innych domen. Aby uzyskać listę wszystkich domen, kliknij ten link.


1
działa dobrze - małe przypomnienie: Próbowałem usunąć „ && t.Namespace == @namespace” - co z tego powodu dało mi wszystkie zestawy .net :-)
— Netsi1964

@ Netsi1964 jeśli usunąć && t.Namespace == @namespacemożna uzyskać wszystkie zajęcia z wszystkich zespołów , w tym .NET jest. GetAssembliesda ci wszystkie zespoły i GetAssemblies().SelectMany(t => t.GetTypes())da wszystkie typy (klasy, struktury itp.) ze wszystkich zespołów.
— nawfal

Uaktualniłem do DotNet Core 2.2 (od 2.1) i ten kod przestał działać dla mojego konkretnego zestawu. Żądany zestaw nie był w żadnym miejscu w kodzie, więc nie został załadowany! W 2.1 został załadowany, ale 2.2 wydaje się mieć opóźnione ładowanie?
— Harvey

@Harvey Czy .NET Core ma na początku domenę?
— nawfal

@nawfal Tak. Ten kod działał wcześniej w 2.1. Stwierdziłem, że wymuszam ładowanie zestawu za pomocą Assembly.Load(nameof(NameOfMyNamespace))dobrze działającego.
— 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: Powyższy kod ilustruje, co się dzieje. Gdybyś go wdrożył, możesz użyć uproszczonej wersji:

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
Nie próbuję być wredny, ale istnieje całkowicie niepotrzebna lista i iteracja przez wszystkie znalezione elementy w tym kodzie; zmienna „classlist” i przeglądanie poprzez „listę nazw” nie zapewniają żadnej funkcji innej niż zwracanie „listy nazw”
— TheXenocide 19.09.08

10
@ TheXenocide cel próbki kodu nie zawsze ma na celu pokazać „najlepszy” sposób pisania kodu, ale jasno przekazać, jak coś się robi.
— Ryan Farley,

4
Po prostu wskazywałem na to ze względu na edukację; naszym obowiązkiem jest sprawienie, aby ludzie materialni uczyli się z najlepszego możliwego przykładu, zamiast ryzykować zły przykład, który negatywnie wpływa na zrozumienie. Nie twierdzę, że w szczególności jest to szkodliwe, ale nie zgadzam się z sentymentem
— TheXenocide

4
Głosuję nad odpowiedzią, jeżeli nie jest to pomocne w zadanym pytaniu. Wskazówka, którą widzisz po najechaniu kursorem na przycisk głosowania w górę / w dół, mówi „To było pomocne”. Dla mnie decyzja o głosowaniu w górę / w dół zależy od tego, czy była pomocna w udzieleniu odpowiedzi na zadane pytanie.
— Ryan Farley

3
Używanie dwóch list i dwóch iteracji pomogło mi tylko spowolnić, próbując dowiedzieć się, dlaczego użyłeś dwóch list, a nie tylko od razu classlistpo pierwszej iteracji nad asm.GetTypes()wynikiem.
— ProfK

20

Dla konkretnego zestawu, NameSpace i 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();

Uwaga: Projekt musi odwoływać się do zestawu


12

Oto poprawka błędów LoaderException, które prawdopodobnie znajdziesz, jeśli jeden z typów podklasuje typ w innym zestawie:

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

To powinno pomóc w ładowaniu typów zdefiniowanych w innych złożeniach.

Mam nadzieję, że to pomaga!


Na pewno wygląda na pomocny, mniej pomocny i mniej mylący niż kod Ryana Farleya, nawet bez zastanowienia.
— ProfK

Przez pewien czas też mnie zdezorientowałeś. Nadal mogę tylko zgadywać, że te elementy Assembly areprezentują normalne przetwarzanie, które może spowodować uruchomienie tego zdarzenia. Nie widzę sensu aw pomaganiu w LoaderExceptionbłędach. Czy mam rację?
— ProfK

9

Nie będziesz w stanie uzyskać wszystkich typów w przestrzeni nazw, ponieważ przestrzeń nazw może łączyć wiele zestawów, ale możesz zebrać wszystkie klasy w zestawie i sprawdzić, czy należą one do tego obszaru nazw.

Assembly.GetTypes()działa na lokalnym zespole lub możesz najpierw załadować zespół, a następnie go wywołać GetTypes().


2
+1 za poprawną odpowiedź. AppDomain.CurrentDomain.GetAssembliesmoże być pomocny.
— nawfal

... a następnie przeglądaj je, odfiltrowując te, które nie pasują do przestrzeni nazw.
— TJ Crowder

OP specjalnie poprosił o „klasy w przestrzeni nazw”, podczas gdy to powoduje, że „piszesz w asemblerze” - więc ta odpowiedź jest niepełna. Prawidłowa odpowiedź to prawdopodobnie ta , która wylicza tylko klasy ze wszystkich zestawów.
— mindplay.dk

6

Podobnie jak odpowiedź @aku, ale przy użyciu metod rozszerzenia:

string @namespace = "...";

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

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

5

Uzyskaj wszystkie klasy według części nazwy Przestrzeni nazw w jednym wierszu:

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

3

Przestrzenie nazw są w rzeczywistości raczej pasywne w projektowaniu środowiska wykonawczego i służą przede wszystkim jako narzędzia organizacyjne. Pełna nazwa typu w .NET składa się z przestrzeni nazw i Class / Enum / Etc. łączny. Jeśli chcesz przejść tylko przez określony zestaw, po prostu przejrzyj typy zwrócone przez zestaw. GetExportedTypes () sprawdza wartość typu. Nazw . Jeśli próbujesz przejść przez wszystkie zestawy załadowane w bieżącej AppDomain, wymagałoby to użycia 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 

Jaka jest AttributeClassnazwa MustHaveAttributes? Nie widzę nic związanego z testowaniem, czy klasa ma atrybuty, czy nie. Jest to bardziej mylące niż pomocne.
— ProfK

1

Całkiem proste

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

I po prostu nie odpowiada na pytanie. Wszystko to polega na uzyskaniu listy wszystkich typów w jednym zestawie, niezależnie od konkretnej przestrzeni nazw.
— Ian
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.