문제

질문에 기초한 질문 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);
    }
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top