如何在运行时优雅地将 IEnumerable 转换为 HashSet而无需事先了解 T

本文关键字:HashSet 了解 转换 运行时 IEnumerable | 更新日期: 2023-09-27 18:30:54

简而言之,我需要在编译时不知道T的情况下将IEnumerable list(值为 IEnumerable<T>)转换为HashSet<T> set。我认为可以做到的唯一方法是以下方法,但我觉得它非常丑陋。

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>也没有)。

如何在运行时优雅地将 IEnumerable<T> 转换为 HashSet<T>而无需事先了解 T

这应该可以做到:

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

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

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