Uzyskiwanie wszystkich typów w przestrzeni nazw poprzez odbicie

268

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

HB
źródło
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));
aku
źródło
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.

nawfal
źródło
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);
}
Ryan Farley
źródło
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

John Peters
źródło
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!

Tsimon
źródło
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().

FlySwat
źródło
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));
JoanComasFdz
źródło
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();
Ivo Stoyanov
źródło
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 ()

TheXenocide
źródło
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
źródło
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)
{
}
Antonio Lopes
źródło
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