创建编译时未知类型的 ImmutableList
Creating an ImmutableList of a type that is unknown at compile-time
给定 Collection<T>
其类型 T
仅在运行时已知(不是在编译时),我想生成一个 ImmutableList<T>
.
我想创建的方法可能像这样:
var immutableList = CreateImmutableList(originalList, type);
其中 originalList 是 IEnumerable
,type 是生成的 ImmutableList<T>
.
的 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 });
}
}
}
如果您准备好装箱/拆箱,那么您可以这样做
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);
}
注意: 如果您的列表很大并且元素类型是属于 boxing/unboxing
的值类型,则可能会对性能产生影响
你可以使用反射来做到这一点:
- 为
ImmutableList<T>
创建正确的 Type
对象
- 用数据填充它
- Return它
这里有一个LINQPad程序演示。我假设 "immutable list" 你的意思是 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
您可以使用 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 });
给定 Collection<T>
其类型 T
仅在运行时已知(不是在编译时),我想生成一个 ImmutableList<T>
.
我想创建的方法可能像这样:
var immutableList = CreateImmutableList(originalList, type);
其中 originalList 是 IEnumerable
,type 是生成的 ImmutableList<T>
.
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 });
}
}
}
如果您准备好装箱/拆箱,那么您可以这样做
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);
}
注意: 如果您的列表很大并且元素类型是属于 boxing/unboxing
的值类型,则可能会对性能产生影响你可以使用反射来做到这一点:
- 为
ImmutableList<T>
创建正确的 - 用数据填充它
- Return它
Type
对象
这里有一个LINQPad程序演示。我假设 "immutable list" 你的意思是 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
您可以使用 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 });