回答:
そのとおり。リフレクションの使用:
static IEnumerable<Type> GetTypesWithHelpAttribute(Assembly assembly) {
foreach(Type type in assembly.GetTypes()) {
if (type.GetCustomAttributes(typeof(HelpAttribute), true).Length > 0) {
yield return type;
}
}
}
Select
拡張メソッドを記述しました。コンパイラーはSelect
、を使用しているために呼び出した場合と同じように、ステートマシンを生成しますyield return
。最後に、ほとんどの場合に得られる可能性のあるパフォーマンスの向上は、マイクロ最適化です。
そうですね、現在のアプリドメインに読み込まれているすべてのアセンブリのすべてのクラスを列挙する必要があります。これを行うには、あなたが呼ぶようなGetAssemblies
方法でAppDomain
、現在のアプリケーションドメインのインスタンスを。
そこから、GetExportedTypes
(パブリックタイプのみが必要な場合)またはGetTypes
それぞれAssembly
を呼び出して、アセンブリに含まれているタイプを取得します。
次に、各インスタンスでGetCustomAttributes
拡張メソッドを呼び出し、Type
検索する属性のタイプを渡します。
LINQを使用してこれを簡略化できます。
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>() };
上記のクエリは、属性が適用された各タイプと、それに割り当てられた属性のインスタンスを取得します。
アプリケーションドメインに多数のアセンブリが読み込まれている場合、その操作には負荷がかかることに注意してください。次のように、Parallel LINQを使用して、操作の時間を短縮できます。
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>() };
特定のものでそれをフィルタリングすることAssembly
は簡単です:
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>() };
アセンブリに多数の型がある場合は、Parallel LINQを再び使用できます。
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>() };
他の回答はGetCustomAttributesを参照しています。IsDefinedの使用例としてこれを追加する
Assembly assembly = ...
var typesWithHelpAttribute =
from type in assembly.GetTypes()
where type.IsDefined(typeof(HelpAttribute), false)
select type;
すでに述べたように、反射は進むべき道です。これを頻繁に呼び出す場合は、結果をキャッシュすることを強くお勧めします。リフレクション、特にすべてのクラスの列挙は非常に遅くなる可能性があるためです。
これは、読み込まれたすべてのアセンブリのすべてのタイプを実行する私のコードのスニペットです。
// 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.
}
}
}
これは、認められたソリューションに加えてパフォーマンスを向上させるものです。非常に多くのクラスがあるため、すべてのクラスが低速になる可能性があります。場合によっては、アセンブリのタイプを確認せずに、アセンブリ全体を除外することができます。
たとえば、自分で宣言した属性を探している場合、システムDLLにその属性を持つ型が含まれているとは限りません。Assembly.GlobalAssemblyCacheプロパティは、システムDLLをすばやくチェックする方法です。これを実際のプログラムで試したところ、30,101種類をスキップでき、1,983種類しかチェックする必要がありませんでした。
フィルタリングする別の方法は、Assembly.ReferencedAssembliesを使用することです。おそらく、特定の属性を持つクラスが必要であり、その属性が特定のアセンブリで定義されている場合は、そのアセンブリとそれを参照する他のアセンブリのみを考慮します。私のテストでは、これはGlobalAssemblyCacheプロパティのチェックよりもわずかに役立ちました。
これらの両方を組み合わせて、さらに速くなりました。以下のコードには両方のフィルターが含まれています。
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)
以下の場合、ポータブル.NETの制限、次のコードは動作するはずです:
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;
}
または、ループ状態ベースを使用する多数のアセンブリの場合yield return
:
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;
}
}
}
}