Skip to content

Instantly share code, notes, and snippets.

@cajuncoding
Last active October 16, 2024 19:48
Show Gist options
  • Save cajuncoding/c02c5161da146f047df579273cdb4df6 to your computer and use it in GitHub Desktop.
Save cajuncoding/c02c5161da146f047df579273cdb4df6 to your computer and use it in GitHub Desktop.
Extensions for non-generic Cast(), ToArray() & ToList() that work with Type variables (vs Generic Type parameters)
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Threading.Tasks;
using Fasterflect;
namespace CajunCoding
{
public static class TypeCache
{
public static readonly Type StringType = typeof(string);
public static readonly Type EnumerableType = typeof(Enumerable);
}
/// <summary>
/// A set of non-generic extensions for IEnumerable to support Cast(), ToArray(), and ToList() conversions using Type variables
/// instead of Generic type parameters. This uses the really great Fasterflect library under the hood to ensure very high performance for the
/// reflection operations.
///
/// Even though Fasterflect is a dependency, if you are using with reflection operations such as this then you should certainly be using Fasterflect
/// as it can dramatically improve performance while greatly simplifying your code at the same time!
/// </summary>
public static class IEnumerableListConversionExtensions
{
/// <summary>
/// Cast the enumerable (non-generic and/or unknown type) to an IEnumerable using Cast&lt;T&gt; for the Type variable specified.
/// This is different from the generic invocation of ToArray&lt;T&gt; because it allows dynamic conversion when the type is not known at compile time.
/// The result of this can be cast to a known T[] without casting errors which would occur if you had object[];
/// </summary>
/// <param name="source"></param>
/// <param name="outputType"></param>
/// <returns></returns>
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);
/// <summary>
/// Convert the enumerable (non-generic and/or unknown type) to a list using ToArray() for the Type variable specified.
/// This is different from the generic invocation of ToArray&lt;T&gt; because it allows dynamic conversion when the type is not known at compile time.
/// The result of this can be cast to a known T[] without casting errors which would occur if you had object[];
/// </summary>
/// <param name="source"></param>
/// <param name="outputType"></param>
/// <returns></returns>
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;
/// <summary>
/// Convert the enumerable (non-generic and/or unknown type) to a list using ToList() for the Type variable specified.
/// This is different from the generic invocation of ToList&lt;T&gt; because it allows dynamic conversion when the type is not known at compile time.
/// The result of this can be cast to a known List&lt;T&gt; without casting errors which would occur if you had &lt;object&gt;
/// </summary>
/// <param name="source"></param>
/// <param name="outputType"></param>
/// <returns></returns>
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;
}
}
@cajuncoding
Copy link
Author

cajuncoding commented Oct 16, 2024

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);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment