[英]Creating an ImmutableList of a type that is unknown at compile-time
给定一个类型T
仅在运行时(而不是在编译时)已知的Collection<T>
,我想生成一个ImmutableList<T>
。
我想创建的方法可能像:
var immutableList = CreateImmutableList(originalList, type);
其中originalList是IEnumerable
和类型是T
所生成的ImmutableList<T>
怎么样?!
(我正在使用 NET .Core)
编辑:感谢评论,我找到了一个有效的解决方案。 它使用 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 });
}
}
}
您可以使用反射来做到这一点:
ImmutableList<T>
创建正确的Type
对象这是一个演示的LINQPad程序。 我假设“不可变列表”是指System.Collections.Immutable.ImmutableList<T>
可通过 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;
}
输出:
System.Collections.Immutable.ImmutableList`1[System.Int32]
1
2
3
4
5
如果你准备好装箱/拆箱,那么你可以做这样的事情
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);
}
注意:如果您的列表很大并且元素类型是属于装箱/拆箱的值类型,则可能会对性能产生影响
您可以使用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 });
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.