MSDN 예제를 기반으로 한 질문 .
독립 실행 형 데스크톱 응용 프로그램에 HelpAttribute가있는 C # 클래스가 있다고 가정하겠습니다. 그러한 속성을 가진 모든 클래스를 열거 할 수 있습니까? 이런 식으로 수업을 인식하는 것이 합리적입니까? 사용자 정의 속성은 가능한 메뉴 옵션을 나열하는 데 사용되며, 항목을 선택하면 해당 클래스의 화면 인스턴스가 나타납니다. 클래스 / 항목의 수는 느리게 증가하지만이 방법으로 다른 곳에서는 열거하지 않아도됩니다.
답변
네 그럼요. 리플렉션 사용 :
static IEnumerable<Type> GetTypesWithHelpAttribute(Assembly assembly) {
foreach(Type type in assembly.GetTypes()) {
if (type.GetCustomAttributes(typeof(HelpAttribute), true).Length > 0) {
yield return type;
}
}
}
답변
현재 앱 도메인에로드 된 모든 어셈블리의 모든 클래스를 열거해야합니다. 이를 위해 현재 앱 도메인 의 인스턴스 에서 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;
}
}
}
}
답변
Andrew의 답변을 개선하고 모든 것을 하나의 LINQ 쿼리로 변환 할 수 있습니다.
public static IEnumerable<Type> GetTypesWithHelpAttribute(Assembly assembly)
{
return assembly.GetTypes().Where(type => type.GetCustomAttributes(typeof(HelpAttribute), true).Length > 0);
}