【问题标题】:Creating an ImmutableList of a type that is unknown at compile-time创建一个在编译时未知的类型的 ImmutableList
【发布时间】:2016-06-16 11:10:15
【问题描述】:

给定一个 Collection<T> 其类型 T 仅在运行时(而不是在编译时)已知,我想生成一个 ImmutableList<T>

我想创建的方法可能是这样的:

var immutableList = CreateImmutableList(originalList, type);

其中 originalList 是 IEnumerable,type 是生成的 ImmutableList<T>T

怎么样?!

(我正在使用 NET .Core)

编辑:感谢 cmets,我找到了一个可行的解决方案。它使用 AddRange 方法。

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

【问题讨论】:

  • 这行不通。要么您在编译时知道类型,这使得创建 Collection&lt;MyType&gt; 变得容易,要么您不知道类型,在这种情况下您不能指望编译器猜测您在运行时提供的内容。
  • CreateImmutableList 必须返回 object,因为它不能返回 ImmutableList&lt;T&gt;(毕竟,我们不知道 T )。那是你要的吗?在这种情况下,请明确说明 var 应该是什么类型。
  • originalList 在运行时也是 IEnumerable&lt;T&gt; 吗?
  • 另外,你说的是System.Collections.Immutable.ImmutableList&lt;T&gt;吗?
  • 是的!那!不可变列表

标签: c# .net immutability .net-core immutable-collections


【解决方案1】:

您可以使用反射来做到这一点:

  1. ImmutableList&lt;T&gt; 创建正确的Type 对象
  2. 用数据填充它
  3. 退货

这是一个演示的LINQPad 程序。我假设“不可变列表”是指通过 Nuget 提供的 System.Collections.Immutable.ImmutableList&lt;T&gt;

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

输出:

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

1
2
3
4
5

【讨论】:

  • 酷!但是当 collection 是 ArrayList 或 Collection 类型时它不起作用
  • 它适用于Collection&lt;T&gt;,但不适用于 ArrayList,因为 ArrayList 不是通用的。你到底为什么要使用 ArrayList?
  • 编辑支持IEnumerable(不带&lt;T&gt;),它将处理ArrayList。由于我使用Collection&lt;T&gt; 进行了测试并且它有效,因此您需要告诉我您测试的内容。
  • 很好,但我们在删除 AddRange 调用时损失了更多性能:(
  • 请看一下我编辑的问题(使用 AddRange)。我希望你审查它!
【解决方案2】:

如果你准备好装箱/拆箱,那么你可以这样做

           var objectCollection = new Collection<object>();
            objectCollection.Add(3);
            objectCollection.Add(4);
            var immutableList = objectCollection.ToImmutableList();

这里的元素类型是int,我们将值添加到对象集合中。如果我们想取回输入的值,我们可以这样做:

    foreach (var obj in immutableList)
                {
                    int myVal = (int) Convert.ChangeType(obj, typeof(int));
                    Console.WriteLine(myVal);
                }

注意:如果您的列表很大并且元素类型是属于装箱/拆箱的值类型,则可能会对性能产生影响

【讨论】:

    【解决方案3】:

    您可以使用CreateRange。为了尽量减少反射,请使用辅助方法:

    private static ImmutableList<T> CreateImmutableList<T>(IEnumerable<object> source)
      => ImmutableList.CreateRange(source.Cast<T>());
    
    private MethodInfo _createImmutableList = typeof(ReflectionExtensions)
      .GetMethod(nameof(CreateImmutableList), BindingFlags.NonPublic | BindingFlags.Static);
    
    // then: 
    return _createImmutableList 
      .MakeGenericMethod(elementType)
      .Invoke(null, new object [] { collection });
    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-17
      • 2020-11-19
      • 2010-11-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多