如何在运行时优雅地将 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>
也没有)。
这应该可以做到:
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;
}