tags:

views:

1289

answers:

4

Hello,

I think I am having a mental block but can someone please enlighten me on how to combine these two LINQ statements into one?

/// <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>();
}

I am avoiding IsAssignableFrom as its seems to fail when not supplying the specific type of the generic interface therefore I believe using FullName caparison over IsAssignableFrom should suffice, for example:

namespace Davide
{
    interface IOutput<TOutputType> { }

    class StringOutput : IOutput<string> { }
}

typeof(IOutput<>).FullName will return "Davide+IOutput`1"

typeof(StringOutput).GetInterfaces()[0].FullName will return "Davide+IOutput`1[[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]"

Therefore using FullName.Contains should suffice

+1  A: 

will this do:

    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();
    }
Hath
A: 

Can I suggest another solution?

    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>();
    }
bruno conde
Thanks Bruno, check out the comment I made on Jon Skeet's post about IsAssignableFrom, you may find it of interest.
Student for Life
+3  A: 

SelectMany translates to a second "from":

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;

I've split conditions into multiple "where" clauses for subjective clarity, btw.

This compiles, but I haven't tested to see whether it actually works or not :) As another answer has shown, you could use "Any" with GetInterfaces() instead of the final "from" clause.

Note that there's no need to keep calling ToList() all over the place - LINQ is designed to be able to work on sequences throughout.

By the way, I'm not sure why you're going through type.GetInterfaces() to check. Is there anything different (and desirable) from that vs using Type.IsAssignableFrom? That would make it simpler:

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;

Do you actually have the same interface type name in a different assembly somewhere?

Jon Skeet
I originally had IsAssignableFrom but discovered that when you supply a generic interface with no specified type e.g. typeof(IInterface<>), it fails as the actual classes implement specific types e.g ConcreteClass : IInterface<string>, hence they don't match.
Student for Life
@Davide: Comparing by name like that won't work either then. If you think it does, please give a full example :)
Jon Skeet
@Jon: I updated the question with an example (hopefully it makes sense), let me know if you think I am missing something with FullName comparison. To be perfectly honest its not my preferred method, but I'm not sure on an alternative yet.
Student for Life
Ah, I'd missed that you're using FullName.Contains, not FullName ==. Personally I'd write a separate method to do that "properly" (using GetGenericTypeDefinition) but if you're happy with FullName, the first query above should work.
Jon Skeet
Jon, I have posted my own answer with the final solution, I found my alternative (yay) and decided to use GetGenericTypeDefinition as discussed. Thanks for the discussion it helped.
Student for Life
A: 

After a short discussion with Jon Skeet and a bit more thought, I have posted the following answer. I altered the method to utilise GetGenericTypeDefinition rather than FullName.Contains, this would be a more robust solution. I also altered the LINQ query Where clauses for IsAbstract and IsInterface as these did not exclude types as expected. Thanks everyone for your feedback.

/// <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>();
}
Student for Life