Reputation: 36048
Perhaps the question title is incorrect. I have the following variables
IEnumerable x = // some IEnumerable
System.Type y = // some type
How can iterate over x in order to generate an array with items of type y?
When I look into the internet I found:
public T[] PerformQuery<T>(IEnumerable q)
{
T[] array = q.Cast<T>().ToArray();
return array;
}
Note I cannot call that method PerformQuery becuase y
is of type System.Type
in other words calling it as PerformQuery<typeof(y)>(x);
or PerformQuery<y>(x);
will give me a compiler error.
Here is the reason why I have that problem. I have web service where I post to it two things. The type of table I will like to query (example typeof(Customer)), and the actual string query example "Select * from customers"
protected void Page_Load(object sender, EventArgs e)
{
// code to deserialize posted data
Type table = // implement that here
String query = // the query that was posted
// note DB is of type DbContext
IEnumerable q = Db.Database.SqlQuery(table, query );
// here I will like to cast q to an array of items of type table!
Upvotes: 4
Views: 8469
Reputation: 969
While the accepted answer here is good and has no dependencies it can have some significant performance impacts as it compiles the expression on every execution.
And if you you are doing reflection operations like this then you should probably be using a nice library like Fasterflect to greatly simplify as well as provide huge performance improvements by encapsulating the complexities of caching, expression building/compiling, etc.
So using Fasterflect this can be done pretty easily with some extension methods such as:
public static class TypeCache
{
public static readonly Type StringType = typeof(string);
public static readonly Type EnumerableType = typeof(Enumerable);
}
public static IEnumerable Cast(this IEnumerable source, Type outputType)
//NOTE: Cast<TResult>() is an extension method that is actually defined on the Enumerable static class which is how we need to reference it...
=> (IEnumerable)TypeCache.EnumerableType.CallMethod(genericTypes: [outputType], name: nameof(Enumerable.Cast), source);
public static Array ToArray(this IEnumerable source, Type outputType)
=> source is not null
//NOTE: ToArray<TSource>() is an extension method that is actually defined on the Enumerable static class which is how we need to reference it...
? (Array)TypeCache.EnumerableType.CallMethod(genericTypes: [outputType], name: nameof(Enumerable.ToArray), source.Cast(outputType))
: null;
public static IList ToList(this IEnumerable source, Type outputType)
=> source is not null
//NOTE: ToArray<TSource>() is an extension method that is actually defined on the Enumerable static class which is how we need to reference it...
? (IList)TypeCache.EnumerableType.CallMethod(genericTypes: [outputType], name: nameof(Enumerable.ToList), source.Cast(outputType))
: null;
Usage of this is now very very simple....
//Contrived example where you have two classes MyClass and MySubClass, which inherits from MyClass...
var list = new List<MyClass> { new(1), new(2), new(3) };
var objectEnumerable = (IEnumerable)intList.Select(i => (object)i);
subClassType = typeof(MySubClass);
//Usage
var subClassEnumerable = objectEnumerable.Cast(subClassType);
//or
var subClassArray = objectEnumerable.ToArray(subClassType);
//or
var subClassList = objectEnumerable.ToList(subClassType);
Also available in my Gist here: https://gist.github.com/cajuncoding/c02c5161da146f047df579273cdb4df6
Upvotes: 0
Reputation: 73183
This is a lot simpler if you use C#'s dynamic dispatch, using the dynamic
keyword. You dont even need fancy methods or, even the Type
you have. You can directly use your method. So if you have
public T[] PerformQuery<T>(IEnumerable<T> q)
{
T[] array = q.ToArray();
return array;
}
Just call
IEnumerable x = // some IEnumerable
dynamic y = x;
IEnumerable z = PerformQuery(y);
Upvotes: -1
Reputation: 9
According to my understanding, IENumerable contains only one type. If I understand what you're trying to do, IENumerable already only contains only objects of type y. If y needs to change, you can write an extension method:
public static T[] ToArray<T>(this IEnumerable<T> source)
{
int length = System.Linq.Enumerable.Count(source);
T[] newArray = new T[length];
int i = 0;
foreach(T item in source)
{
newArray[i] = item;
}
return newArray;
}
Upvotes: 0
Reputation: 125630
You can use Expression Trees:
public static class MyExtensions
{
public static Array ToArray(this IEnumerable source, Type type)
{
var param = Expression.Parameter(typeof(IEnumerable), "source");
var cast = Expression.Call(typeof(Enumerable), "Cast", new[] { type }, param);
var toArray = Expression.Call(typeof(Enumerable), "ToArray", new[] { type }, cast);
var lambda = Expression.Lambda<Func<IEnumerable, Array>>(toArray, param).Compile();
return lambda(source);
}
}
It generates x => x.Cast<Type>().ToArray()
for you, with Type
known at runtime.
Usage:
IEnumerable input = Enumerable.Repeat("test", 10);
Type type = typeof(string);
Array result = input.ToArray(type);
Upvotes: 6
Reputation: 44038
var ObjectsOfType_y = x.OfType<object>().Where(x => x.GetType() == y);
Notice that this will return an IEnumerable<object>
, though. There's no way around that because the type that y
(Type) represents is unknown at compile time.
Upvotes: 5