C# 如何优雅地转换IEnumerable<;T>;到HashSet<;T>;在运行时不事先知道T

C# 如何优雅地转换IEnumerable<;T>;到HashSet<;T>;在运行时不事先知道T,c#,reflection,C#,Reflection,简而言之,我需要将IEnumerable列表(值为IEnumerable)转换为HashSet,而不需要在编译时知道T。我认为唯一可以做到的方法是如下所示,但我发现它非常难看 public IEnumerable GetHashSet(IEnumerable source) { Type itemType = source.GetType().GetGenericArguments()[0]; Type listOpen = typeof(List<>); T

简而言之,我需要将
IEnumerable列表
(值为
IEnumerable
)转换为
HashSet
,而不需要在编译时知道
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;
}
public IEnumerable GetHashSet(IEnumerable源代码)
{
类型itemType=source.GetType().GetGenericArguments()[0];
类型listOpen=typeof(列表);
类型listClosed=listOpen.MakeGenericType(新类型[]{itemType});
IList list=Activator.CreateInstance(listClosed)作为IList;
foreach(源中的var obj)
列表。添加(obj);
类型hashSetOpen=typeof(HashSet);
类型hashSetClosed=hashSetOpen.MakeGenericType(新类型[]{itemType});
将Activator.CreateInstance(hashSetClosed,list)返回为IEnumerable;
}
问题是,
HashSet
无法通过一些非通用接口添加对象(相反,
List
具有
IList.Add(object)
)。此外,它没有接受“裸”IEnumerable的构造函数(也没有
List

这应该可以做到:

public IEnumerable<T> GetHashSet<T>(IEnumerable<T> source)
{
    return new HashSet<T>(source);
}
public IEnumerable GetHashSet(IEnumerable源代码)
{
返回新的哈希集(源);
}
这应该可以做到:

public IEnumerable<T> GetHashSet<T>(IEnumerable<T> source)
{
    return new HashSet<T>(source);
}
public IEnumerable GetHashSet(IEnumerable源代码)
{
返回新的哈希集(源);
}

原始答案: 如果您希望坚持方法签名,您可以这样做:

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;
}
私有静态IEnumerable GetHashSet(IEnumerable源)
{
var type=source.GetType().GetGenericArguments()[0];
var ctor=typeof(HashSet).MakeGenericType(type)
.GetConstructor(新[]{typeof(IEnumerable).MakeGenericType(type)});
将ctor.Invoke(新对象[]{source})作为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;
}
私有静态IEnumerable GetHashSet(IEnumerable源)
{
var inputType=source.GetType();
如果(!inputType.IsGenericType | | inputType.IsGenericTypeDefinition)
抛出新ArgumentException(nameof(source));
var genericArgumentType=inputType.GetGenericArguments()[0];
var iEnumerableType=typeof(IEnumerable);
如果(!iEnumerableType.IsAssignableFrom(inputType))
抛出新ArgumentException(nameof(source));
var-ctor=typeof(HashSet).MakeGenericType(genericArgumentType)
.GetConstructor(新[]{iEnumerableType});
if(ctor==null)
抛出新异常(“未找到ctor”);
将ctor.Invoke(新对象[]{source})作为IEnumerable返回;
}

原始答案: 如果您希望坚持方法签名,您可以这样做:

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;
}
私有静态IEnumerable GetHashSet(IEnumerable源)
{
var type=source.GetType().GetGenericArguments()[0];
var ctor=typeof(HashSet).MakeGenericType(type)
.GetConstructor(新[]{typeof(IEnumerable).MakeGenericType(type)});
将ctor.Invoke(新对象[]{source})作为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;
}
私有静态IEnumerable GetHashSet(IEnumerable源)
{
var inputType=source.GetType();
如果(!inputType.IsGenericType | | inputType.IsGenericTypeDefinition)
抛出新ArgumentException(nameof(source));
var genericArgumentType=inputType.GetGenericArguments()[0];
var iEnumerableType=typeof(IEnumerable);
如果(!iEnumerableType.IsAssignableFrom(inputType))
抛出新ArgumentException(nameof(source));
var-ctor=typeof(HashSet).MakeGenericType(genericArgumentType)
.GetConstructor(新[]{iEnumerableType});
if(ctor==null)
抛出新异常(“未找到ctor”);
将ctor.Invoke(新对象[]{source})作为IEnumerable返回;
}

为什么不将该方法设置为泛型(
IEnumerable GetHashSet(IEnumerable source)
)?如果你不这样做,你就不得不采取这种“丑陋”的方法。还有,你真正的问题是“我怎样才能让这段代码不那么难看”?当然,您可以完全跳过列表,通过找到合适的
add
方法,直接将
source
中的项目添加到HashSet中。谢谢。编译时我不知道t,所以我不能使用带有此签名的方法,也不能使用
Add
方法(因为,是什么类型)。看起来,您试图避免重复(因为您将哈希集返回为IEnumerable)。如果是这样的话,可以看看或。我建议您为
ICollection
创建一个不安全的包装器,它实现
ICollection
和强制转换。使用反射和内部集合创建此对象的实例,然后通过
ICollection.Add
方法添加每个对象。@Oliver,谢谢,不是这样的。之所以使用HashSet,是因为EntityFramework 6似乎更喜欢实体上的集合。代码是针对一个非常一般的类的。CrudController,尝试将页面上任何多选列表中的字符串ID转换为EF实体上的更新关系,无论其类型如何。为什么不将该方法设置为泛型(
IEnumerable GetHashSet(IEnumerable source)
)?如果你不这样做,你就不得不采取这种“丑陋”的方法。还有,你真正的问题是“我怎样才能让这段代码不那么难看”?当然,您可以完全跳过列表,通过找到合适的
add
方法,直接将
source
中的项目添加到HashSet中。谢谢。编译时我不知道t,所以我不能使用带有此签名的方法,也不能使用