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

Popular posts from this blog

c++ - Convert big endian to little endian when reading from a binary file -

C#: Application without a window or taskbar item (background app) that can still use Console.WriteLine() -

unicode - Are email addresses allowed to contain non-alphanumeric characters? -