【问题标题】:How to find out if an object's type implements IEnumerable<X> where X derives from Base using Reflection如何找出对象的类型是否实现了 IEnumerable<X>,其中 X 使用反射从 Base 派生
【发布时间】:2010-03-30 18:02:04
【问题描述】:

给一个基类Base,我想写一个方法Test,像这样:

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

如果 o 的类型实现 IEnumerable&lt;X&gt; 的任何接口,其中 X 派生自 Base,则 Test 返回 true,这样如果我会这样做:

public static IEnumerable<string> Convert(IEnumerable enumerable)
{
    if (Test(enumerable))
    {
        return enumerable.Cast<Base>().Select(b => b.SomePropertyThatIsString);
    }

    return enumerable.Cast<object>().Select(o => o.ToString());
}

...它会做正确的事,使用反射。我确信这是遍历该类型的所有接口以找到符合要求的第一个接口的问题,但我很难在其中找到通用的IEnumerable&lt;&gt;

当然,我可以考虑这个:

public static IEnumerable<string> Convert(IEnumerable enumerable)
{
    return enumerable.Cast<object>().Select(o => o is Base ? ((Base)o).SomePropertyThatIsString : o.ToString());
}

...但可以将其视为思想实验。

【问题讨论】:

  • 你的代码sn-ps没有意义,“o”从何而来?
  • 也许你应该阅读 lambda 表达式。

标签: c# linq reflection


【解决方案1】:

您还可以使用如下所示的LINQ 查询。

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

此代码假定以下 using 语句:

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

因为这只是一个思想实验。这是另一种作为扩展方法的实现。

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;
    }
}

【讨论】:

  • 而不是 arg.BaseType == typeof(MyBaseClass),你可以说 typeof(MyBaseClass).IsAssignableFrom(arg),对吧?
  • IsAssignableFrom` 更好,因为 == 只有在类型直接从 MyBaseClass 继承时才有效
  • @Thomas:是的,我更喜欢这样。编辑了我的答案以反映这一点。
【解决方案2】:

您可以使用Type.FindInterfaces 过滤掉该类型实现的所有IEnumerable&lt;&gt; 接口,并检查每个接口的泛型参数(通过Type.GetGenericArguments),看看它是Base 还是继承自Base .

更新:这是一些示例代码:

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;
        }
    }
}

【讨论】:

  • 这就是我的想法,但是我如何检查一个接口是否是通用接口 IEnumerable 的实例?
【解决方案3】:

我最近编写了一些需要遍历任何集合的代码。

作为一个遗留的 .NET 应用程序,我什至没有可用的泛型!

这是摘录:

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;
    }
}

我发现即使是 .NET 1.1 集合类(如 SqlParameterCollection)也是 IEnumerable。
它还捕获通用集合,例如 List,因为它们也是 IEnumerable。

希望这对某人有所帮助。

【讨论】:

  • 这个问题是针对泛型的。此外,您可以简单地使用objects is IEnumerableobjects as IEnumerable 来测试它是否可枚举。您的解决方案非常复杂。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多