How to find out if an object's type implements IEnumerable where X derives from Base using Reflection

后端 未结 3 1334
孤街浪徒
孤街浪徒 2021-01-19 03:49

Give a base class Base, I want to write a method Test, like this:

private static bool Test(IEnumerable enumerable)
{
...
}

suc

相关标签:
3条回答
  • 2021-01-19 03:55

    I've written some code recently that needs to iterate over any collection.

    As a legacy .NET app, I didn't even have generics available to me!

    Here's an extract :

    var t = objects.GetType(); // to be compatible with the question
    
    bool isIEnumerable = false;
    foreach (var i in t.GetInterfaces())
    {
        if (i == typeof(IEnumerable))
        {
            isIEnumerable = true;
            break;
        }
    }
    

    I found that even the .NET 1.1 collection classes such as SqlParameterCollection were IEnumerable.
    It also catches generic collections such as List<> as those too are IEnumerable.

    Hope this helps someone.

    0 讨论(0)
  • 2021-01-19 04:05

    You can use the Type.FindInterfaces to filter out all the IEnumerable<> interfaces the type implements and check the generic parameters (through Type.GetGenericArguments) on each of them to see if it's Base or inherits from Base.

    Update: Here's some sample code:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Reflection;
    
    namespace ConsoleApplication1
    {
        class Base
        {
            string Prop { get; set;}
        }
    
        class A : Base
        {
        }
    
        class Test : List<A>
        {
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                try
                {
                    Test test = new Test();
                    Type myType = test.GetType();
    
                    //string filterCriteria = "IEnumerable`1";
                    Type typeA = Type.GetType("ConsoleApplication1.A");
                    string filterCriteria = "System.Collections.Generic.IEnumerable`1[[" +
                                            typeA.AssemblyQualifiedName +
                                            "]]";
    
                    // Specify the TypeFilter delegate that compares the 
                    // interfaces against filter criteria.
                    TypeFilter myFilter = new TypeFilter(MyInterfaceFilter);
                    Type[] myInterfaces = myType.FindInterfaces(myFilter,
                        filterCriteria);
                    if (myInterfaces.Length > 0)
                    {
                        Console.WriteLine("\n{0} implements the interface {1}.",
                            myType, filterCriteria);
                        for (int j = 0; j < myInterfaces.Length; j++)
                            Console.WriteLine("Interfaces supported: {0}.",
                                myInterfaces[j].ToString());
                    }
                    else
                        Console.WriteLine(
                            "\n{0} does not implement the interface {1}.",
                            myType, filterCriteria);
                }
                catch (ArgumentNullException e)
                {
                    Console.WriteLine("ArgumentNullException: " + e.Message);
                }
                catch (TargetInvocationException e)
                {
                    Console.WriteLine("TargetInvocationException: " + e.Message);
                }
                catch (Exception e)
                {
                    Console.WriteLine("Exception: " + e.Message);
                }
            }
    
            public static bool MyInterfaceFilter(Type typeObj, Object criteriaObj)
            {
                // This will be true, if criteria is
                // System.Collections.Generic.IEnumerable`1[[ConsoleApplication1.A, ConsoleApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]
                if (typeObj.FullName == criteriaObj.ToString())
                    return true;
                // This will be true, if criteria is
                // IEnumerable`1
                // You will still need to check the generic parameters on the original type
                    // (generic parameters are not exposed on Type instances for interfaces
                else if (typeObj.Name == criteriaObj.ToString())
                    return true;
                else
                    return false;
            }
        }
    }
    
    0 讨论(0)
  • 2021-01-19 04:06

    You could also use a LINQ query that could look like this.

    public static bool ImplementsBaseType(IEnumerable objects)
    {
        int found = ( from i in objects.GetType().GetInterfaces()
                     where i.IsGenericType && 
                           i.GetGenericTypeDefinition() == typeof(IEnumerable<>) &&
                           typeof(MyBaseClass).IsAssignableFrom(i.GetGenericArguments()[0])
                     select i ).Count();
    
        return (found > 0);
    }
    

    This code assumes the following using statements:

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    

    Since this is just a thought experiment. Here is another implementation as an extension method.

    public static class ConversionAssistants
    {
        public static bool GenericImplementsType(this IEnumerable objects, Type baseType)
        {
            foreach (Type type in objects.GetType().GetInterfaces())
            {
                if (type.IsGenericType)
                {
                    if (type.GetGenericTypeDefinition() == typeof(IEnumerable<>))
                    {
                        if (baseType.IsAssignableFrom(type.GetGenericArguments()[0]))
                            return true;
                    }
                }
            }
            return false;
        }
    }
    
    0 讨论(0)
提交回复
热议问题