如何将以下代码转换为 C# 中的编译表达式?
How can I convert the following code to a Compiled Expression in C#?
我有一段代码负责使用反射创建通用列表。
public static IList MakeList(Type listType)
{
// checks to ensure listType is a generic list omitted...
// gets the generic argument e.g. string for a List<string>
var genericType = listType.GetGenericArguments()[0];
return (IList)typeof(List<>)
.MakeGenericType(genericType)
.GetConstructor(Type.EmptyTypes)
.Invoke(null);
}
我可以通过以下方式调用:
var stringList = MakeList(typeof(IList<string>));
var intList = MakeList(typeof(IList<int>));
或者例如来自 PropertyInfo
:
MekList(propInfo.PropertyType);
请注意,由于类型可能来自 PrpertyInfo
,例如如果 属性 的类型是 IList<string>
那么使用 Activator.CreateInstance(propType)
会抛出异常:Cannot create an instance of an interface.
如何将其转换为编译表达式以从性能优势中获益?
您需要 NewExpression。示例(我还没有测试过):
private static Dictionary<Type, Func<IList>> funcs;
public static IList MakeList(Type listType)
{
Func<IList> f;
// checks to ensure listType is a generic list omitted...
if(!funcs.TryGetValue(listType, out f)) {
// gets the generic argument e.g. string for a List<string>
var genericType = listType.GetGenericArguments()[0];
var ctor = typeof(List<>)
.MakeGenericType(genericType)
.GetConstructor(Type.EmptyTypes);
f = Expression.Lambda<Func<IList>>(
Expression.New(ctor, new Expression[]{})).Compile();
funcs[listType] = f;
}
return f();
}
确保测量性能以检查这是否真的比简单地使用 Activator.CreateInstance.
更快
感谢 Random832
我最终得到了:
public static class New
{
public static readonly Func<Type, IList> MakeList = t => MakeListImpl(t)();
private static readonly IDictionary<Type, Func<IList>> Cache = new Dictionary<Type, Func<IList>>();
private static Func<IList> MakeListImpl(Type listType)
{
Func<IList> result;
if (!Cache.TryGetValue(listType, out result))
{
Ensure.That(listType.IsGenericList());
var genericArg = listType.GetGenericArguments()[0];
var concreteType = typeof(List<>).MakeGenericType(genericArg);
result = Expression.Lambda<Func<IList>>(Expression.New(concreteType)).Compile();
Cache[listType] = result;
}
return result;
}
}
基准测试(发布版本 | 平均 1000 次运行)
1,000,000
------------------------------------
Native: 00:00:00.0103980
ActivatorGeneric: 00:00:00.1274370
ActivatorType: 00:00:00.1115370
CompiledNew: 00:00:00.0261892
10,000,000
------------------------------------
Native: 00:00:00.1040899
ActivatorGeneric: 00:00:01.2864721
ActivatorType: 00:00:01.1627026
CompiledNew: 00:00:00.2432613
NewListNative => list = new List<string>()
NewListActivatorGeneric => list = System.Activator.CreateInstance<List<string>>()
NewListActivatorType => list = (List<string>) System.Activator.CreateInstance(typeof(List<string>))
CompiledNew => list = (List<string>)New.MakeList(typeof(List<string>))
如果你总是使用typeof(...)
,你也可以使用这样的通用方法:
public static IList<T> MakeList<T>()
{
return new List<T>();
}
你这样使用它:
var stringList = MakeList<string>();
var intList = MakeList<int>();
如果在编译时不知道类型,就不能写成你想要的样子。您可以使用 Expression 在您的代码片段中调用相同的方法,但它们是相同的。尝试 Activator.CreateInstance(typeof(WhatEver))
.
我测试了你的代码和 Activator class CreateInstace 方法 10000 个实例 100 次,结果是:
- 对于 MakeList:13 毫秒
- 对于 CreateInstance 0.8 毫秒
我有一段代码负责使用反射创建通用列表。
public static IList MakeList(Type listType)
{
// checks to ensure listType is a generic list omitted...
// gets the generic argument e.g. string for a List<string>
var genericType = listType.GetGenericArguments()[0];
return (IList)typeof(List<>)
.MakeGenericType(genericType)
.GetConstructor(Type.EmptyTypes)
.Invoke(null);
}
我可以通过以下方式调用:
var stringList = MakeList(typeof(IList<string>));
var intList = MakeList(typeof(IList<int>));
或者例如来自 PropertyInfo
:
MekList(propInfo.PropertyType);
请注意,由于类型可能来自 PrpertyInfo
,例如如果 属性 的类型是 IList<string>
那么使用 Activator.CreateInstance(propType)
会抛出异常:Cannot create an instance of an interface.
如何将其转换为编译表达式以从性能优势中获益?
您需要 NewExpression。示例(我还没有测试过):
private static Dictionary<Type, Func<IList>> funcs;
public static IList MakeList(Type listType)
{
Func<IList> f;
// checks to ensure listType is a generic list omitted...
if(!funcs.TryGetValue(listType, out f)) {
// gets the generic argument e.g. string for a List<string>
var genericType = listType.GetGenericArguments()[0];
var ctor = typeof(List<>)
.MakeGenericType(genericType)
.GetConstructor(Type.EmptyTypes);
f = Expression.Lambda<Func<IList>>(
Expression.New(ctor, new Expression[]{})).Compile();
funcs[listType] = f;
}
return f();
}
确保测量性能以检查这是否真的比简单地使用 Activator.CreateInstance.
更快感谢 Random832
我最终得到了:
public static class New
{
public static readonly Func<Type, IList> MakeList = t => MakeListImpl(t)();
private static readonly IDictionary<Type, Func<IList>> Cache = new Dictionary<Type, Func<IList>>();
private static Func<IList> MakeListImpl(Type listType)
{
Func<IList> result;
if (!Cache.TryGetValue(listType, out result))
{
Ensure.That(listType.IsGenericList());
var genericArg = listType.GetGenericArguments()[0];
var concreteType = typeof(List<>).MakeGenericType(genericArg);
result = Expression.Lambda<Func<IList>>(Expression.New(concreteType)).Compile();
Cache[listType] = result;
}
return result;
}
}
基准测试(发布版本 | 平均 1000 次运行)
1,000,000
------------------------------------
Native: 00:00:00.0103980
ActivatorGeneric: 00:00:00.1274370
ActivatorType: 00:00:00.1115370
CompiledNew: 00:00:00.0261892
10,000,000
------------------------------------
Native: 00:00:00.1040899
ActivatorGeneric: 00:00:01.2864721
ActivatorType: 00:00:01.1627026
CompiledNew: 00:00:00.2432613
NewListNative => list = new List<string>()
NewListActivatorGeneric => list = System.Activator.CreateInstance<List<string>>()
NewListActivatorType => list = (List<string>) System.Activator.CreateInstance(typeof(List<string>))
CompiledNew => list = (List<string>)New.MakeList(typeof(List<string>))
如果你总是使用typeof(...)
,你也可以使用这样的通用方法:
public static IList<T> MakeList<T>()
{
return new List<T>();
}
你这样使用它:
var stringList = MakeList<string>();
var intList = MakeList<int>();
如果在编译时不知道类型,就不能写成你想要的样子。您可以使用 Expression 在您的代码片段中调用相同的方法,但它们是相同的。尝试 Activator.CreateInstance(typeof(WhatEver))
.
我测试了你的代码和 Activator class CreateInstace 方法 10000 个实例 100 次,结果是:
- 对于 MakeList:13 毫秒
- 对于 CreateInstance 0.8 毫秒