通过反射获取所有ICollection属性

4

我想要从未知类型的类中获取所有ICollection<T>属性。此外,类型T(集合所属的类型)在编译时不可知。首先,我尝试了以下方法:

foreach (var property in entity.GetType().GetProperties())
{
    if (typeof(ICollection).IsAssignableFrom(property.PropertyType) || typeof(ICollection<>).IsAssignableFrom(property.PropertyType))
    {
        // do something
    }
}

但它不能正常工作(即使对于ICollection属性也会评估为false)。

我这样让它可以工作:

foreach (var property in entity.GetType().GetProperties())
{
    var getMethod = property.GetGetMethod();
    var test = getMethod.Invoke(entity, null);
    if (test is ICollection)
    {
        // do something
    }
}

但我不想执行所有的getter。为什么第一段代码不起作用?如何在不执行所有getter的情况下找到ICollection属性?


2
ICollectionICollection<T>是不同的类型。你真正感兴趣的是哪一个? - Jon Skeet
我只对ICollection<T>属性感兴趣(例如:public virtual ICollection<Note> Notes { get; set; })。我试图解决在使用EF和POCO(分离)对象时添加/更新具有多对多关系的对象的问题。为此,我需要获取任何类(未知)的ICollection属性,然后获取该属性的值,即集合的元素。 - xx77aBs
反射速度非常慢。你考虑过使用T4进行代码生成吗?然而,这种方法感觉很XY,我怀疑你可以通过泛型解决大部分实际问题...另外,你有没有考虑如何处理循环关系? - Aron
2个回答

5

事实证明,使用IsAssignableFrom检查无法确定接口是否是另一个接口的派生类:

Console.WriteLine(typeof(ICollection<>).IsAssignableFrom(typeof(ICollection<Int32>)));     
Console.WriteLine(typeof(ICollection<Int32>).IsAssignableFrom(typeof(ICollection<>)));

将会同时写入false

这里的少量帮助下,这是我能想到的最佳解决方案:

    static IEnumerable<PropertyInfo> GetICollectionOrICollectionOfTProperties(this Type type)
    {    
        // Get properties with PropertyType declared as interface
        var interfaceProps =
            from prop in type.GetProperties()
            from interfaceType in prop.PropertyType.GetInterfaces()
            where interfaceType.IsGenericType
            let baseInterface = interfaceType.GetGenericTypeDefinition()
            where (baseInterface == typeof(ICollection<>)) || (baseInterface == typeof(ICollection))
            select prop;

        // Get properties with PropertyType declared(probably) as solid types.
        var nonInterfaceProps =
            from prop in type.GetProperties()
            where typeof(ICollection).IsAssignableFrom(prop.PropertyType) || typeof(ICollection<>).IsAssignableFrom(prop.PropertyType)
            select prop;

        // Combine both queries into one resulting
        return interfaceProps.Union(nonInterfaceProps);                
    }

这种解决方案可能会产生一些重复项(虽然几乎不可能,但为了确保,请使用Distinct),并且看起来不太好看。但是在具有接口返回类型和具体返回类型属性的类上,它运行良好。
    class Collections
    {
        public List<Int32> ListTProp
        {
            get;
            set;
        }

        public IDictionary<Int32, String> IDictionaryProp
        {
            get;
            set;
        }

        public ICollection ICollectionProp
        {
            get;
            set;
        }

        public ICollection<DateTime> IDateTimeCollectionProp
        {
            get;
            set;
        }
    }

@Askolein 或许它并没有解决 OP 面临的确切问题,或者以他不愿使用的方式解决了。又或者,他只是忘记将其标记为已接受。谁知道呢。 - Eugene Podskal

1

尝试使用已接受的答案后,我遇到了一个情况:只有部分匹配项被返回。我的对象有3个ICollection<T>属性,但只返回了2个。我花了一些时间进行测试和尝试弄清楚原因,但最终选择放弃并编写了以下代码:

public static IEnumerable<PropertyInfo> GetICollectionProperties(object entity) 
{
    return entity.GetType().GetProperties()
            .Where(p => p.PropertyType.IsGenericType
            && p.PropertyType.GetGenericTypeDefinition() == typeof(ICollection<>));
}

我已经使用相同的测试用例进行了测试,从该方法返回了正确的结果。
这不会捕捉非泛型ICollections,但是OP确实要求ICollection<T>属性,虽然可以很容易地重构以包括它们。它也不会返回不完全是ICollection类型的属性(例如,在Eugene的测试用例中,List和IDictionary将不会被返回(但再次,这是OP想要的))。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接