c# - Need a Better Way Than Reflection -
i'm reading csv file , records recorded string[]. want take each record , convert custom object.
t getmyobject<t>();
currently i'm doing through reflection slow. i'm testing 515 meg file several million records. takes under 10 seconds parse. takes under 20 seconds create custom objects using manual conversions convert.tosometype
around 4 minutes conversion objects through reflection.
what way handle automatically?
it seems lot of time spent in propertyinfo.setvalue
method. tried caching properties methodinfo
setter , using instead, slower.
i have tried converting delegate great jon skeet suggested here: improving performance reflection , alternatives should consider, problem don't know property type ahead of time. i'm able delegate
var myobject = activator.createinstance<t>(); foreach( var property in typeof( t ).getproperties() ) { var d = delegate.createdelegate( typeof( action<,> ) .makegenerictype( typeof( t ), property.propertytype ), property.getsetmethod() ); }
the problem here can't cast delegate concrete type action<t, int>
, because property type of int
isn't known ahead of time.
the first thing i'd write sample code manually tells absolute best case can expect - see if current code worth fixing.
if using propertyinfo.setvalue
etc, absolutely can make quicker, juts object
- hyperdescriptor might start (it significantly faster raw reflection, without making code more complicated).
for optimal performance, dynamic il methods way go (precompiled once); in 2.0/3.0, maybe dynamicmethod
, in 3.5 i'd favor expression
(with compile()
). let me know if want more detail?
implementation using expression
, csvreader
, uses column headers provide mapping (it invents data along same lines); uses ienumerable<t>
return type avoid having buffer data (since seem have quite lot of it):
using system; using system.collections.generic; using system.globalization; using system.io; using system.linq; using system.linq.expressions; using system.reflection; using lumenworks.framework.io.csv; class entity { public string name { get; set; } public datetime dateofbirth { get; set; } public int id { get; set; } } static class program { static void main() { string path = "data.csv"; inventdata(path); int count = 0; foreach (entity obj in read<entity>(path)) { count++; } console.writeline(count); } static ienumerable<t> read<t>(string path) t : class, new() { using (textreader source = file.opentext(path)) using (csvreader reader = new csvreader(source,true,delimiter)) { string[] headers = reader.getfieldheaders(); type type = typeof(t); list<memberbinding> bindings = new list<memberbinding>(); parameterexpression param = expression.parameter(typeof(csvreader), "row"); methodinfo method = typeof(csvreader).getproperty("item",new [] {typeof(int)}).getgetmethod(); expression invariantculture = expression.constant( cultureinfo.invariantculture, typeof(iformatprovider)); for(int = 0 ; < headers.length ; i++) { memberinfo member = type.getmember(headers[i]).single(); type finaltype; switch (member.membertype) { case membertypes.field: finaltype = ((fieldinfo)member).fieldtype; break; case membertypes.property: finaltype = ((propertyinfo)member).propertytype; break; default: throw new notsupportedexception(); } expression val = expression.call( param, method, expression.constant(i, typeof(int))); if (finaltype != typeof(string)) { val = expression.call( finaltype, "parse", null, val, invariantculture); } bindings.add(expression.bind(member, val)); } expression body = expression.memberinit( expression.new(type), bindings); func<csvreader, t> func = expression.lambda<func<csvreader, t>>(body, param).compile(); while (reader.readnextrecord()) { yield return func(reader); } } } const char delimiter = '\t'; static void inventdata(string path) { random rand = new random(123456); using (textwriter dest = file.createtext(path)) { dest.writeline("id" + delimiter + "dateofbirth" + delimiter + "name"); (int = 0; < 10000; i++) { dest.write(rand.next(5000000)); dest.write(delimiter); dest.write(new datetime( rand.next(1960, 2010), rand.next(1, 13), rand.next(1, 28)).tostring(cultureinfo.invariantculture)); dest.write(delimiter); dest.write("fred"); dest.writeline(); } dest.close(); } } }
second version (see comments) uses typeconverter
rather parse
:
using system; using system.collections.generic; using system.componentmodel; using system.globalization; using system.io; using system.linq; using system.linq.expressions; using system.reflection; using lumenworks.framework.io.csv; class entity { public string name { get; set; } public datetime dateofbirth { get; set; } public int id { get; set; } } static class program { static void main() { string path = "data.csv"; inventdata(path); int count = 0; foreach (entity obj in read<entity>(path)) { count++; } console.writeline(count); } static ienumerable<t> read<t>(string path) t : class, new() { using (textreader source = file.opentext(path)) using (csvreader reader = new csvreader(source, true, delimiter)) { string[] headers = reader.getfieldheaders(); type type = typeof(t); list<memberbinding> bindings = new list<memberbinding>(); parameterexpression param = expression.parameter(typeof(csvreader), "row"); methodinfo method = typeof(csvreader).getproperty("item", new[] { typeof(int) }).getgetmethod(); var converters = new dictionary<type, constantexpression>(); (int = 0; < headers.length; i++) { memberinfo member = type.getmember(headers[i]).single(); type finaltype; switch (member.membertype) { case membertypes.field: finaltype = ((fieldinfo)member).fieldtype; break; case membertypes.property: finaltype = ((propertyinfo)member).propertytype; break; default: throw new notsupportedexception(); } expression val = expression.call( param, method, expression.constant(i, typeof(int))); if (finaltype != typeof(string)) { constantexpression converter; if (!converters.trygetvalue(finaltype, out converter)) { converter = expression.constant(typedescriptor.getconverter(finaltype)); converters.add(finaltype, converter); } val = expression.convert(expression.call(converter, "convertfrominvariantstring", null, val), finaltype); } bindings.add(expression.bind(member, val)); } expression body = expression.memberinit( expression.new(type), bindings); func<csvreader, t> func = expression.lambda<func<csvreader, t>>(body, param).compile(); while (reader.readnextrecord()) { yield return func(reader); } } } const char delimiter = '\t'; static void inventdata(string path) { random rand = new random(123456); using (textwriter dest = file.createtext(path)) { dest.writeline("id" + delimiter + "dateofbirth" + delimiter + "name"); (int = 0; < 10000; i++) { dest.write(rand.next(5000000)); dest.write(delimiter); dest.write(new datetime( rand.next(1960, 2010), rand.next(1, 13), rand.next(1, 28)).tostring(cultureinfo.invariantculture)); dest.write(delimiter); dest.write("fred"); dest.writeline(); } dest.close(); } } }
Comments
Post a Comment