Frage

Frage basierend auf MSDN Beispiel .

Lassen Sie uns sagen, dass wir einige C # -Klassen mit HelpAttribute im Standalone-Desktop-Anwendung haben. Ist es möglich, alle Klassen mit einem solchen Attribut aufzuzählen? Ist es sinnvoll, um Klassen auf diese Weise zu erkennen? Benutzerdefinierte Attribut würde verwendet werden, um mögliche Menüoptionen zur Liste, Element auswählen bringt Instanz dieser Klasse zu screenen. Anzahl der Klassen / Einzelteile werden langsam wachsen, aber auf diese Weise vermeiden wir können sie alle an anderer Stelle aufzuzählen, denke ich.

War es hilfreich?

Lösung

Ja, absolut. Mit Reflection:

static IEnumerable<Type> GetTypesWithHelpAttribute(Assembly assembly) {
    foreach(Type type in assembly.GetTypes()) {
        if (type.GetCustomAttributes(typeof(HelpAttribute), true).Length > 0) {
            yield return type;
        }
    }
}

Andere Tipps

Nun, würden Sie haben durch alle Klassen in allen Baugruppen aufzuzählen, die in der aktuellen Anwendungsdomäne geladen werden. Um das zu tun, würde rufen Sie die GetAssemblies Methode auf die AppDomain Instanz für die aktuelle Anwendungsdomäne.

Von dort würde rufen Sie GetExportedTypes oder GetTypes auf jedem Assembly die Arten zu erhalten, die in der Baugruppe enthalten ist .

Dann würde rufen Sie die GetCustomAttributes Methode auf jedes Type Beispiel, vorbei an den Typ des Attributs, das Sie finden möchten .

Sie können LINQ verwenden, um dies zu vereinfachen für Sie:

var typesWithMyAttribute =
    from a in AppDomain.CurrentDomain.GetAssemblies()
    from t in a.GetTypes()
    let attributes = t.GetCustomAttributes(typeof(HelpAttribute), true)
    where attributes != null && attributes.Length > 0
    select new { Type = t, Attributes = attributes.Cast<HelpAttribute>() };

Die obige Abfrage werden Ihnen jede Art mit Ihrem Attribute erhält auf mich angewandt, zusammen mit der Instanz des Attributs (n) zugeordnet es.

Beachten Sie, wenn Sie eine große Anzahl von Baugruppen in Ihre Anwendungsdomäne geladen haben, dass der Betrieb teuer sein könnte. Sie können Parallel LINQ verwenden, um die Zeit der Operation zu reduzieren, wie so :

var typesWithMyAttribute =
    // Note the AsParallel here, this will parallelize everything after.
    from a in AppDomain.CurrentDomain.GetAssemblies().AsParallel()
    from t in a.GetTypes()
    let attributes = t.GetCustomAttributes(typeof(HelpAttribute), true)
    where attributes != null && attributes.Length > 0
    select new { Type = t, Attributes = attributes.Cast<HelpAttribute>() };

Das Filtern sie auf einem bestimmten Assembly ist einfach:

Assembly assembly = ...;

var typesWithMyAttribute =
    from t in assembly.GetTypes()
    let attributes = t.GetCustomAttributes(typeof(HelpAttribute), true)
    where attributes != null && attributes.Length > 0
    select new { Type = t, Attributes = attributes.Cast<HelpAttribute>() };

Und wenn die Anordnung eine große Anzahl von Arten in sich hat, dann können Sie Parallel LINQ wieder:

Assembly assembly = ...;

var typesWithMyAttribute =
    // Partition on the type list initially.
    from t in assembly.GetTypes().AsParallel()
    let attributes = t.GetCustomAttributes(typeof(HelpAttribute), true)
    where attributes != null && attributes.Length > 0
    select new { Type = t, Attributes = attributes.Cast<HelpAttribute>() };

Andere Antworten Referenz GetCustomAttributes . Addiert man diese ein als Beispiel für die Verwendung IsDefined

Assembly assembly = ...
var typesWithHelpAttribute = 
        from type in assembly.GetTypes()
        where type.IsDefined(typeof(HelpAttribute), false)
        select type;

Wie bereits erwähnt, Reflexion ist der Weg zu gehen. Wenn Sie diese gehen häufig zu nennen, ich sehr empfehlen die Ergebnisse zwischenspeichern, wie Reflexion, vor allem durch jede Klasse aufzählt, kann sehr langsam sein.

Dies ist ein Ausschnitt aus meinem Code, der in allen geladenen Baugruppen alle Arten läuft durch:

// this is making the assumption that all assemblies we need are already loaded.
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) 
{
    foreach (Type type in assembly.GetTypes())
    {
        var attribs = type.GetCustomAttributes(typeof(MyCustomAttribute), false);
        if (attribs != null && attribs.Length > 0)
        {
            // add to a cache.
        }
    }
}

Dies ist eine Leistungssteigerung auf der akzeptierte Lösung. obwohl alle Klassen laufen kann langsam sein, weil es so viele sind. Manchmal kann man eine ganze Baugruppe herauszufiltern, ohne auf jeder seiner Art zu suchen.

Zum Beispiel, wenn Sie nach einem Attribut suchen, dass Sie selbst erklärt, Sie nicht erwarten, dass keines der System-DLLs mit diesem Attribut alle Typen enthalten. Die Assembly.GlobalAssemblyCache Eigenschaft ist ein schneller Weg für System-DLLs zu überprüfen. Als ich versuchte, das auf einem realen Programm fand ich, dass ich 30.101 Arten überspringen könnte und ich habe nur 1.983 Arten überprüfen.

Eine weitere Möglichkeit, Filter ist Assembly.ReferencedAssemblies zu verwenden. Vermutlich, wenn Sie Klassen mit einem bestimmten Attribut wollen, und das Attribut wird in einer bestimmten Assembly definiert, dann kümmern Sie nur über diese Montage und andere Baugruppen, die auf sie verweisen. In meinen Tests half dies etwas mehr als die Global Eigenschaft überprüfen.

I kombiniert beides und haben es sogar noch schneller. Der folgende Code enthält beiden Filter.

        string definedIn = typeof(XmlDecoderAttribute).Assembly.GetName().Name;
        foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
            // Note that we have to call GetName().Name.  Just GetName() will not work.  The following
            // if statement never ran when I tried to compare the results of GetName().
            if ((!assembly.GlobalAssemblyCache) && ((assembly.GetName().Name == definedIn) || assembly.GetReferencedAssemblies().Any(a => a.Name == definedIn)))
                foreach (Type type in assembly.GetTypes())
                    if (type.GetCustomAttributes(typeof(XmlDecoderAttribute), true).Length > 0)

Bei der Tragbarer .NET Einschränkungen , sollte der folgende Code funktioniert:

    public static IEnumerable<TypeInfo> GetAtributedTypes( Assembly[] assemblies, 
                                                           Type attributeType )
    {
        var typesAttributed =
            from assembly in assemblies
            from type in assembly.DefinedTypes
            where type.IsDefined(attributeType, false)
            select type;
        return typesAttributed;
    }

oder für eine große Anzahl von Baugruppen schlaufenzustandsbasierten yield return verwendet:

    public static IEnumerable<TypeInfo> GetAtributedTypes( Assembly[] assemblies, 
                                                           Type attributeType )
    {
        foreach (var assembly in assemblies)
        {
            foreach (var typeInfo in assembly.DefinedTypes)
            {
                if (typeInfo.IsDefined(attributeType, false))
                {
                    yield return typeInfo;
                }
            }
        }
    }

Wir können auf Andrew Antwort verbessern und das Ganze in eine LINQ-Abfrage konvertieren.

    public static IEnumerable<Type> GetTypesWithHelpAttribute(Assembly assembly)
    {
        return assembly.GetTypes().Where(type => type.GetCustomAttributes(typeof(HelpAttribute), true).Length > 0);
    }
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top