Reputation: 597
In short, I need to convert IEnumerable list
(with value of IEnumerable<T>
) to HashSet<T> set
without knowing T
at compilation time. The only way I figured it can be done is as following, but I find it extremely ugly.
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;
}
The problem is, HashSet<T>
does not have any way of adding an object via some non-generic interface (in contrast, List<T>
has IList.Add(object)
). Also it does not have a constructor that takes a "bare" IEnumerable (neither does List<T>
).
Upvotes: 2
Views: 6893
Reputation: 4216
Original answer: You can do it this way if you want to insist on your method signature:
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;
}
Improved: As mentioned in the comments, often it is better to be more explicit about what a function is expected to do, so i added the necessary checks:
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;
}
Upvotes: 5
Reputation: 18649
This should do it:
public IEnumerable<T> GetHashSet<T>(IEnumerable<T> source)
{
return new HashSet<T>(source);
}
Upvotes: 11