Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating an ImmutableList of a type that is unknown at compile-time

Given a Collection<T> whose type T is only known in runtime (not at compile time), I would like to generate an ImmutableList<T>.

The method I would like to create may like like:

var immutableList = CreateImmutableList(originalList, type);

where originalList is IEnumerable and type is the T of of the generated ImmutableList<T>.

How?!

(I'm working with NET .Core)

EDIT: Thanks to the comments I have found a working solution. It uses AddRange method.

namespace Sample.Tests
{
    using System;
    using System.Collections;
    using System.Collections.Immutable;
    using System.Collections.ObjectModel;
    using System.Linq;
    using System.Reflection;
    using Xunit;

    public class ImmutabilityTests
    {
        [Fact]
        public void CollectionCanBeConvertedToImmutable()
        {
            var original = new Collection<object>() { 1, 2, 3, 4, };
            var result = original.AsImmutable(typeof(int));

            Assert.NotEmpty(result);
            Assert.IsAssignableFrom<ImmutableList<int>>(result);
        }
    }

    public static class ReflectionExtensions
    {
        public static IEnumerable AsImmutable(this IEnumerable collection, Type elementType)
        {
            var immutableType = typeof(ImmutableList<>).MakeGenericType(elementType);
            var addRangeMethod = immutableType.GetMethod("AddRange");
            var typedCollection = ToTyped(collection, elementType);

            var emptyImmutableList = immutableType.GetField("Empty").GetValue(null);
            emptyImmutableList = addRangeMethod.Invoke(emptyImmutableList, new[] { typedCollection });
            return (IEnumerable)emptyImmutableList;
        }

        private static object ToTyped(IEnumerable original, Type type)
        {
            var method = typeof(Enumerable).GetMethod("Cast", BindingFlags.Public | BindingFlags.Static).MakeGenericMethod(type);
            return method.Invoke(original, new object[] { original });
        }
    }
}
like image 924
SuperJMN Avatar asked Dec 03 '25 17:12

SuperJMN


1 Answers

You can do this using reflection:

  1. Create the right Type object for ImmutableList<T>
  2. Fill it with data
  3. Return it

Here is a LINQPad program that demonstrates. I'm assuming that by "immutable list" you mean System.Collections.Immutable.ImmutableList<T> available through Nuget:

void Main()
{
    object il = CreateImmutableList(new[] { 1, 2, 3, 4, 5 }, typeof(int));
    il.GetType().Dump();
    il.Dump();
}

public static object CreateImmutableList(IEnumerable collection, Type elementType)
{
    // TODO: guard clauses for parameters == null
    var resultType = typeof(ImmutableList<>).MakeGenericType(elementType);
    var result = resultType.GetField("Empty").GetValue(null);
    var add = resultType.GetMethod("Add");
    foreach (var element in collection)
        result = add.Invoke(result, new object[] { element });
    return result;
}

Output:

System.Collections.Immutable.ImmutableList`1[System.Int32]

1
2
3
4
5
like image 198
Lasse V. Karlsen Avatar answered Dec 06 '25 06:12

Lasse V. Karlsen



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!