사용자 정의 클래스 속성으로 모든 클래스를 열거하는 방법은 무엇입니까?
-
03-07-2019 - |
문제
질문에 기초한 질문 MSDN 예.
독립형 데스크탑 응용 프로그램에서 HelpTribute가 포함 된 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>() };
위의 쿼리는 속성에 적용된 속성을 사용하여 각 유형을 가져옵니다.
응용 프로그램 도메인에 많은 수의 어셈블리가로드 된 경우 해당 작업이 비쌀 수 있습니다. 당신이 사용할 수있는 평행 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. 사용의 예로 이것을 추가합니다 정의되었습니다
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 개의 유형 만 확인하면됩니다.
필터를 필터링하는 또 다른 방법은 어셈블리를 사용하는 것입니다. 아마도 특정 속성을 가진 클래스를 원하고 해당 속성이 특정 어셈블리에 정의되어 있으면 해당 어셈블리 및 기타 어셈블리에만 관심이 있습니다. 내 테스트에서 이것은 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);
}