在一组值中查找共享类型

时间:2016-05-23 07:36:37

标签: c# .net reflection

给定传入的IEnumerable,是否有任何内置方法可以为IEnumerable中的所有项返回最佳匹配的基类型,它处理可空/不可空类型和继承?

这样的事情:

var hs = new HashSet<object> {1,2,3,null};
Type t = GetSharedType(hs); //returns typeof(int?)

hs = new HashSet<object> {new BaseClass(), new DerivedClass()};
t = GetSharedType(hs); //returns typeof(BaseClass)

hs = new HashSet<object> {"1", 1};
t = GetSharedType(hs); //returns typeof(object) or null

(我知道我可以写自己的;我的问题是,如果有内置的东西)。

2 个答案:

答案 0 :(得分:2)

不,没有内置机制来执行此操作。您可以合并多个reflection APIs

首先,您可以使用GetType()检索集合中每个对象的实际类型:

IEnumerable<Type> realTypes = hs.Select(o => o.GetType());

现在,您将收集具有Type属性和BaseType方法的GetInterfaces()类。 Ypu可以使用this code来获取每种类型的所有层次结构:

public static IEnumerable<Type> GetParentTypes(this Type type)
{
    // is there any base type?
    if ((type == null) || (type.BaseType == null))
    {
        yield break;
    }

    // return all implemented or inherited interfaces
    foreach (var i in type.GetInterfaces())
    {
        yield return i;
    }

    // return all inherited types
    var currentBaseType = type.BaseType;
    while (currentBaseType != null)
    {
        yield return currentBaseType;
        currentBaseType= currentBaseType.BaseType;
    }
}

您可以使用它来获取层次结构的集合:

IEnumerable<IEnumerable<Type>> baseTypes = realTypes.Select(t => t.GetParentTypes());

下一步是将所有此列表合并为仅具有相交的值。您可以使用Enumerable.Intersect方法和this代码执行此操作:

public static IEnumerable<T> IntersectAllIfEmpty<T>(params IEnumerable<T>[] lists)
{
    IEnumerable<T> results = null;

    lists = lists.Where(l => l.Any()).ToArray();

    if (lists.Length > 0)
    {
        results = lists[0];

        for (int i = 1; i < lists.Length; i++)
            results = results.Intersect(lists[i]);
    }
    else
    {
        results = new T[0];
    }

    return results;
}

最后,我们有:

IEnumerable<Type> realTypes = hs.Select(o => o.GetType());
IEnumerable<IEnumerable<Type>> baseTypes = realTypes.Select(t => t.GetParentTypes());
IEnumerable<Type> inersectedBaseTypes = IntersectAllIfEmpty(baseTypes);

然后我们可以使用Type.IsAssignableFrom()方法迭代每个类型并确保其中一个类型只能从它们自己分配:

Type mostConcreteType = inersectedBaseTypes.Where(t => inersectedBaseTypes.Count(bt => t.IsAssignableFrom(bt)) == 1).FirstOrDefault();

答案 1 :(得分:-1)

,没有内置类型可以执行此操作。正如你自己确定的那样,你必须自己写作。像

这样的东西
public static Type GetEnumerableType<T>(this IEnumerable<T> enumerable)
{
    return typeof(T);
}