对于任何大小的表格,下面的代码都非常慢。 (100,1000等...)罪魁祸首是使用new T()
实例化我的对象。请注意,这不是我最终确定的代码,我只是将其中的部分内容删除,以便更容易地进行分析。一旦我将代码重构成形状,实例化和初始化就会一起发生。
有没有办法加快速度?我可能会忘记一些非常简单的事情,或者我可能会被剔除。希望是前者。
public static IList<T> ToList<T>(this DataTable table) where T : Model, new()
{
T[] entities = new T[table.Rows.Count];
// THIS LOOP IS VERY VERY SLOW
for (int i = 0; i < table.Rows.Count; i++)
entities[i] = new T();
// THIS LOOP IS FAST
for (int i = 0; i < table.Rows.Count; i++)
entities[i].Init(table, table.Rows[i]);
return new List<T>(entities);
}
编辑以获取更多信息:
任何给定ModelType
的构造函数将如下所示:
public ModelType()
{
_modelInfo = new ModelTypeInfo();
}
任何给定ModelTypeInfo
的构造函数将只设置一些字符串和字符串[]值,而该类的唯一作用是提供设置的值。
修改以获取更多信息:
因为它似乎是一个热门话题,所以在打破对象构造和初始化之前,这是我的方法对于实数的看法:
public static IList<T> ToList<T>(this DataTable table, ModelInfo modelInfo) where T : Model, new()
{
var tempRepository = new Repository<T>(modelInfo);
var list = new List<T>();
foreach (DataRow row in table.Rows)
list.Add(tempRepository.FromData(table, row));
return list;
}
答案 0 :(得分:13)
在幕后,new T()
会生成对System.Activator.CreateInstance<T>()
的调用,这是(反射性地)缓慢:
L_0012: ldc.i4.0
L_0013: stloc.1
L_0014: br.s L_0026
L_0016: ldloc.0
L_0017: ldloc.1
L_0018: call !!0 [mscorlib]System.Activator::CreateInstance<!!T>()
L_001d: stelem.any !!T
L_0022: ldloc.1
L_0023: ldc.i4.1
L_0024: add
L_0025: stloc.1
您可能希望考虑传递建筑代表。
答案 1 :(得分:3)
您的问题的标题表明这与该方法是通用的事实有关。是否更快地分配相同数量的对象而没有泛型?如果没有,它必须与构造函数中正在进行的任何工作有关。你可以发布构造函数代码吗?
EDITED 这是我前一段时间写的一个用于在DynamicMethod中缓存构造函数的东西,这非常快:
在你班上:
delegate T ConstructorDelegate();
方法体:
DynamicMethod method = new DynamicMethod(string.Empty, typeof(T), null,
MethodBase.GetCurrentMethod().DeclaringType.Module);
ILGenerator il = method.GetILGenerator();
il.Emit(OpCodes.Newobj, type.GetConstructor(Type.EmptyTypes));
il.Emit(OpCodes.Ret);
var constructor = (ConstructorDelegate)method.CreateDelegate(typeof(ConstructorDelegate));
答案 2 :(得分:3)
问题是表达式new T()
实际上在幕后使用了反射。 (它调用Activator.CreateInstance
)因此,每次调用都需要时间。
一种解决方案是约束T来实现ICloneable。然后,您可以编写new T()
一次,并在循环中克隆它。显然,如果您完全控制模型,则只能这样做。
另一个选择是让方法采用创建者委托,如下所示:
public static IList<T> ToList<T>(this DataTable table, Func<T> creator) where T : Model {
T[] entities = new T[table.Rows.Count];
for (int i = 0; i < table.Rows.Count; i++)
entities[i] = creator();
//...
}
然后你会这样称呼它:
table.ToList(() => new MyModelType());
因为它在参数中使用,所以在调用方法时不需要显式指定泛型类型。
最少侵入性的方法是使用LINQ表达式来创建自己的创建者方法。
编辑:像这样:
static class CreatorFactory<T> where T : new() {
public static readonly Func<T> Method =
Expression.Lambda<Func<T>>(Expression.New(typeof(T)).Compile();
}
public static IList<T> ToList<T>(this DataTable table) where T : Model {
var entities = table.Rows.Select(r => CreatorFactory<T>.Method()).ToList();
for (int i = 0; i < table.Rows.Count; i++)
entities[i].Init(table, table.Rows[i]);
return entities;
}
答案 3 :(得分:2)
你真的需要一个列表,或者IEnumerable是否足够好?如果是这样,您可以对对象进行延迟/延迟创建:
public static IEnumerable<T> ToEnumerable<T>(this DataTable table) where T : Model, new()
{
foreach (DataRow row in table.Rows)
{
T entity = new T();
entity.Init(table, row);
yield return entity;
}
}
不幸的是,这仍然可能很慢,因为大部分时间都可能用于构建对象,但它可能允许您将此负载延迟足够长的时间以使应用更快地显示,或者直到你能够完全过滤掉一些物体之后。
另外,您可能会考虑使用类似Factory的模式来实现它:
public static IEnumerable<T> ToEnumerable<T>(this DataTable table, Func<DataRow, T> TFactory)
{
foreach (DataRow row in table.Rows)
{
yield return TFactory(row);
}
}
答案 4 :(得分:0)
您是否正在测试发布版本? tables.loop.count是一个简单的属性,你可以比较它从循环中提升它吗? 实例化T的成本是多少? 创建T是否会分配大量小对象,以便您遇到一些垃圾收集?
答案 5 :(得分:0)
以示例方式显示C#中的此方法:
public T Method<T>() where T : new()
{
return new T();
}
编译为此MSIL代码(来自Reflector):
.method public hidebysig instance !!T Method<.ctor T>() cil managed
{
.maxstack 2
.locals init (
[0] !!T CS$1$0000,
[1] !!T CS$0$0001)
L_0000: nop
L_0001: ldloca.s CS$0$0001
L_0003: initobj !!T
L_0009: ldloc.1
L_000a: box !!T
L_000f: brfalse.s L_001c
L_0011: ldloca.s CS$0$0001
L_0013: initobj !!T
L_0019: ldloc.1
L_001a: br.s L_0021
L_001c: call !!0 [mscorlib]System.Activator::CreateInstance<!!T>()
L_0021: stloc.0
L_0022: br.s L_0024
L_0024: ldloc.0
L_0025: ret
}
为了不进入太多的内部,这里有几个步骤,检查几个条件,初始化数据字段的需要等,最后需要调用Activator 。所有这些都是为了实例化泛型类型对象。是的,这是用来代替直接调用类型的构造函数。
答案 6 :(得分:0)
即使您需要使用列表,为什么还要先创建数组?
public static IList<T> ToList<T>(this DataTable table) where T : Model, new()
{
var list = new List<T>();
foreach (DataRow dr in table.Rows) {
T entity = new T();
entity.Init(table, dr);
list.Add(entity);
}
return list;
}
答案 7 :(得分:0)
对于稍后遇到此问题的任何人,此博文对我非常有帮助:http://blogs.msdn.com/haibo_luo/archive/2005/11/17/494009.aspx
以下是我最终对“工厂”方法所做的更改。 (不是真正合适的工厂,但服务于此目的)
public class Repository<T> : IRepository<T> where T : Model, new()
{
// ...
private delegate T CtorDelegate();
private CtorDelegate _constructor = null;
private CtorDelegate Constructor
{
get
{
if (_constructor == null)
{
Type type = typeof(T);
DynamicMethod dm = new DynamicMethod(type.Name + "Constructor", type, new Type[] { }, typeof(Repository<T>).Module);
ILGenerator ilgen = dm.GetILGenerator();
ilgen.Emit(OpCodes.Nop);
ilgen.Emit(OpCodes.Newobj, type.GetConstructor(Type.EmptyTypes));
ilgen.Emit(OpCodes.Ret);
_constructor = (CtorDelegate)dm.CreateDelegate(typeof(CtorDelegate));
}
return _constructor;
}
}
public T FromData(DataTable table, DataRow row)
{
T model = Constructor(); // was previously = new T();
model.Init(table, row);
return model;
}
}