如何在运行时优雅地将IEnumerable <t>转换为HashSet <t>而不事先知道T.

时间:2016-01-18 15:28:47

标签: c# reflection

简而言之,我需要在编译时不知道IEnumerable listIEnumerable<T>(值HashSet<T> set)转换为T。我认为可以做到的唯一方法如下,但我发现它非常难看。

public IEnumerable GetHashSet(IEnumerable source)
{
    Type itemType = source.GetType().GetGenericArguments()[0];
    Type listOpen = typeof(List<>);
    Type listClosed = listOpen.MakeGenericType(new Type[] { itemType });
    IList list = Activator.CreateInstance(listClosed) as IList;
    foreach (var obj in source)
        list.Add(obj);
    Type hashSetOpen = typeof(HashSet<>);
    Type hashSetClosed = hashSetOpen.MakeGenericType(new Type[] { itemType });
    return Activator.CreateInstance(hashSetClosed, list) as IEnumerable;
}

问题是,HashSet<T>无法通过某些非通用接口添加对象(相反,List<T>具有IList.Add(object))。它也没有一个带有“裸”IEnumerable的构造函数(List<T>也没有)。

2 个答案:

答案 0 :(得分:6)

这应该这样做:

public IEnumerable<T> GetHashSet<T>(IEnumerable<T> source)
{
    return new HashSet<T>(source);
}

答案 1 :(得分:5)

原始回答: 如果你想坚持你的方法签名,你可以这样做:

private static IEnumerable GetHashSet(IEnumerable source)
{
    var type = source.GetType().GetGenericArguments()[0];
    var ctor = typeof(HashSet<>).MakeGenericType(type)
                .GetConstructor(new[] {typeof (IEnumerable<>).MakeGenericType(type)});
    return ctor.Invoke(new object[] { source }) as IEnumerable;
}

<强>改进: 正如评论中所提到的,通常最好更明确地说明函数应该做什么,所以我添加了必要的检查:

private static IEnumerable GetHashSet(IEnumerable source)
{
    var inputType = source.GetType();
    if (!inputType.IsGenericType || inputType.IsGenericTypeDefinition)
        throw new ArgumentException(nameof(source));

    var genericArgumentType = inputType.GetGenericArguments()[0];
    var iEnumerableType = typeof (IEnumerable<>).MakeGenericType(genericArgumentType);

    if (!iEnumerableType.IsAssignableFrom(inputType))
        throw new ArgumentException(nameof(source));

    var ctor = typeof (HashSet<>).MakeGenericType(genericArgumentType)
        .GetConstructor(new[] {iEnumerableType});

    if (ctor == null)
        throw new Exception("ctor not found.");

    return ctor.Invoke(new object[] { source }) as IEnumerable;
}