Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to cast an IEnumerable<object> to an IEnumerable<runtime type>

I'm trying to accomplish the following.

Suppose I have this data model:

public class Article
{
     public ICollection<string> Tags { get; set; }
}

These tags are retrieved from a database. My database's API returns them to me as a List<object>.

Therefore, I need to make a conversion from List<object> to something that implements ICollection<string>.

I am aware of the LINQ Cast<T>() method that cast its elements to the given type and returns the converted IEnumerable<T>.

However, I cannot use Cast<string>() because that would always cast my List<object> to IEnumerable<string>, not giving any options for models that have ICollection<double> properties (or any other type).

I can use reflection and get the generic type parameter:

Type genericArg = collectionType.GetGenericArguments().First();

But that would leave me with a runtime Type, which I cannot use as Cast<genericArg>().

How can I cast an IEnumerable<object> to an IEnumerable of a dynamic Type?.

I should note that no complex types are allowed on my model, so anything like:

public ICollection<Tag> Tags { get; set; }

will not happen. I only handle primitive types.

like image 823
Matias Cicero Avatar asked Feb 09 '23 06:02

Matias Cicero


1 Answers

You have a basic misunderstanding about casting.

The result type of a casting operation must be known at compile time.¹

Consider the following example:

string a = "abc";
object b = (object)a;
string c = (string)b;

The runtime type of a, b and c is the same. It's string. The compile-time type is different. Casting is only relevant for the compile-time type.

Thus, the answer to your question

How to cast an IEnumerable<object> to an IEnumerable<runtime type>

is: You don't. Casting does not make sense for runtime types.


That said, let me offer a solution to your real problem: Let's say you have an IEnumerable<object> values, a Type myTargetType and want to create a List<typeof(myTargetType)> containing the values.

First, you create the list using reflection:

var listType = typeof(List<>).MakeGenericType(myTargetType);
IList myList = (IList)Activator.CreateInstance(listType);

And then you fill the list:

foreach (var item in values)
{
    myList.Add(item);
}

Obviously, Add will throw an ArgumentException if an entry of values is not of runtime type myTargetType.


¹ The result type can be a generic type, but generic type parameters have to be specified at compile time as well.

like image 71
Heinzi Avatar answered Feb 12 '23 01:02

Heinzi