Tono Nam
Tono Nam

Reputation: 36048

IEnumerable to array of T[]

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.


edit

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

Answers (5)

CajunCoding
CajunCoding

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

nawfal
nawfal

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

Jeter
Jeter

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

MarcinJuraszek
MarcinJuraszek

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

Fede
Fede

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

Related Questions