Объединить два запроса LINQ?

Я думаю, что у меня ментальный блок, но может ли кто-нибудь объяснить мне, как объединить эти два оператора LINQ в одно?

/// <summary>
/// Returns an array of Types that implement the supplied generic interface in the
/// current AppDomain.
/// </summary>
/// <param name="interfaceType">Type of generic interface implemented</param>
/// <param name="includeAbstractTypes">Include Abstract class types in the search</param>
/// <param name="includeInterfaceTypes">Include Interface class types in the search</param>
/// <returns>Array of Types that implement the supplied generic interface</returns>
/// <remarks>
/// History.<br/>
/// 10/12/2008      davide       Method creation.<br/>
/// </remarks>
public static Type[] GetTypesImplementingGenericInterface(Type interfaceType, bool includeAbstractTypes, bool includeInterfaceTypes)
{
    // Use linq to find types that implement the supplied interface.
    var allTypes = AppDomain.CurrentDomain.GetAssemblies().ToList()
                    .SelectMany(s => s.GetTypes())
                    .Where(p => p.IsAbstract == includeAbstractTypes  
                                    && p.IsInterface == includeInterfaceTypes);

    var implementingTypes = from type in allTypes
                            from intf in type.GetInterfaces().ToList()
                            where intf.FullName != null && intf.FullName.Contains(interfaceType.FullName)
                            select type;

    return implementingTypes.ToArray<Type>();
}

Я избегаю IsAssignableFrom, так как кажется, что он не работает, если не указан конкретный тип универсального интерфейса, поэтому я полагаю, что использование полного имени FullName вместо IsAssignableFrom должно быть достаточно, например:

namespace Davide
{
    interface IOutput<TOutputType> { }

    class StringOutput : IOutput<string> { }
}

typeof (IOutput <>). FullName вернет "Davide + IOutput`1"

typeof (StringOutput) .GetInterfaces () [0] .ullName вернет "Davide + IOutput`1 [[System.String, mscorlib, версия = 2.0.0.0, культура = нейтральная, PublicKeyToken = b77a5c561934e089]]"

Поэтому использование FullName.Contains должно быть достаточным.

10.12.2008 12:22:10
4 ОТВЕТА

это будет делать:

    public static Type[] GetTypesImplementingGenericInterface(Type interfaceType, bool includeAbstractTypes, bool includeInterfaceTypes)
    {
        // Use linq to find types that implement the supplied interface.
        var allTypes = AppDomain.CurrentDomain.GetAssemblies().ToList()
                                    .SelectMany(s => s.GetTypes())
                                    .Where(p => p.IsAbstract == includeAbstractTypes
                                           && p.IsInterface == includeInterfaceTypes
                                           && p.GetInterfaces().Any(i=>i.FullName != null && i.FullName.Contains(interfaceType.FullName))
                                           );

        //var implementingTypes = from type in allTypes
        //                        from intf in type.GetInterfaces().ToList()
        //                        where intf.FullName != null && intf.FullName.Contains(interfaceType.FullName)
        //                        select type;

        //return implementingTypes.ToArray<Type>();

        return allTypes.ToArray();
    }
1
10.12.2008 12:50:25

Могу ли я предложить другое решение?

    public static Type[] GetTypesImplementingGenericInterface(Type interfaceType, bool includeAbstractTypes, bool includeInterfaceTypes)
    {
        // Use linq to find types that implement the supplied interface.
        var implementingTypes = AppDomain.CurrentDomain.GetAssemblies()
                                    .SelectMany(s => s.GetTypes())
                                    .Where(p => interfaceType.IsAssignableFrom(p)
                                              && (
                                                     (p.IsAbstract && includeAbstractTypes) 
                                                     || (p.IsInterface && includeInterfaceTypes)
                                                     || (!p.IsAbstract && !p.IsInterface)
                                                 )
                                          );

        return implementingTypes.ToArray<Type>();
    }
1
10.12.2008 15:16:47
Спасибо, Бруно, зацените комментарий, который я сделал на пост Джона Скита о IsAssignableFrom, вы можете найти его интересным.
Student for Life 10.12.2008 22:07:14

SelectMany переводит в секунду «от»:

var implementors = from assembly in AppDomain.CurrentDomain.GetAssemblies()
                   from type in assembly.GetTypes()
                   where type.IsAbstract == includeAbstractTypes
                   where type.IsInterface == includeInterfaceTypes
                   from intf in type.GetInterfaces()
                   where intf.FullName != null && 
                         intf.FullName.Contains(interfaceType.FullName)
                   select type;

Я разделил условия на несколько «где» для субъективной ясности, кстати.

Он компилируется, но я не проверял, работает ли он на самом деле или нет :). Как показал другой ответ, вы можете использовать «Any» с GetInterfaces () вместо заключительного предложения «from».

Обратите внимание, что нет необходимости постоянно вызывать ToList () повсюду - LINQ разработан для работы с последовательностями повсюду.

Кстати, я не уверен, почему вы проходите через type.GetInterfaces () для проверки. Есть ли что-то отличное (и желательное) от этого с использованием Type.IsAssignableFrom ? Это сделало бы это проще:

var implementors = from assembly in AppDomain.CurrentDomain.GetAssemblies()
                   from type in assembly.GetTypes()
                   where type.IsAbstract == includeAbstractTypes
                   where type.IsInterface == includeInterfaceTypes
                   where interfaceType.IsAssignableFrom(type)
                   select type;

У вас есть одно и то же имя интерфейса в другой сборке?

3
10.12.2008 13:21:34
Первоначально у меня был IsAssignableFrom, но я обнаружил, что когда вы предоставляете универсальный интерфейс без определенного типа, например typeof (IInterface <>), он завершается ошибкой, так как фактические классы реализуют конкретные типы, например ConcreteClass: IInterface <string>, следовательно, они не совпадают.
Student for Life 10.12.2008 22:06:32
@ Давид: Сравнение по имени так не сработает. Если вы считаете, что это так, приведите полный пример :)
Jon Skeet 10.12.2008 22:55:25
@Jon: я обновил вопрос с примером (надеюсь, это имеет смысл), дайте мне знать, если вы думаете, что я что-то упускаю при сравнении FullName. Честно говоря, это не мой любимый метод, но я не уверен в альтернативе.
Student for Life 11.12.2008 03:36:42
Ах, я пропустил, что вы используете FullName.Contains, а не FullName ==. Лично я бы написал отдельный метод, чтобы сделать это «правильно» (используя GetGenericTypeDefinition), но если вы довольны FullName, первый запрос выше должен работать.
Jon Skeet 11.12.2008 06:21:26
Джон, я отправил свой собственный ответ с окончательным решением, я нашел свой вариант (yay) и решил использовать GetGenericTypeDefinition, как обсуждалось. Спасибо за обсуждение, это помогло.
Student for Life 11.12.2008 14:12:10
РЕШЕНИЕ

После короткого разговора с Джоном Скитом и немного больше размышлений я разместил следующий ответ. Я изменил метод, чтобы использовать GetGenericTypeDefinition вместо FullName.Contains, это было бы более надежное решение. Я также изменил условия запроса LINQ Where для IsAbstract и IsInterface, поскольку они не исключали типы, как и ожидалось. Спасибо всем за ваш отзыв.

/// <summary>
/// Returns an array of Types that implement the supplied generic interface in the
/// current AppDomain.
/// </summary>
/// <param name="interfaceType">Type of generic interface implemented</param>
/// <param name="excludeAbstractTypes">Exclude Abstract class types in the search</param>
/// <param name="excludeInterfaceTypes">Exclude Interface class types in the search</param>
/// <returns>Array of Types that implement the supplied generic interface</returns>
/// <remarks>
/// History.<br/>
/// 11/12/2008      davide       Created method.<br/>
/// 11/12/2008      davide       Altered method to use a two LINQ query pass.<br/>
/// 11/12/2008      davide       Altered method to use optimised combined LINQ query.<br/>
/// 12/12/2008      davide       Altered method and replaced FullName criteria match with GetGenericTypeDefinition.<br/>
/// </remarks>
public static Type[] GetTypesImplementingGenericInterface(Type interfaceType, bool excludeAbstractTypes, bool excludeInterfaceTypes)
{
    if (!interfaceType.IsGenericType)
    {
        throw new ArgumentException("Supplied interface is not a Generic type");
    }

    if (interfaceType.ContainsGenericParameters)
    {
        interfaceType = interfaceType.GetGenericTypeDefinition();
    }

    // Use linq to find types that implement the supplied generic interface.
    var implementingTypes = from assembly in AppDomain.CurrentDomain.GetAssemblies()
                            from type in assembly.GetTypes()
                            where (type.IsAbstract != excludeAbstractTypes) || (!excludeAbstractTypes)
                            where (type.IsInterface != excludeInterfaceTypes) || (!excludeInterfaceTypes)
                            from intf in type.GetInterfaces()
                            where intf.IsGenericType && intf.GetGenericTypeDefinition() == interfaceType
                            select type;

    return implementingTypes.ToArray<Type>();
}
0
11.12.2008 14:08:20