.net - How to cast a value of generic type T to double without boxing? -


imagine following simple code:

public void f<t>(ilist<t> values) t : struct {   foreach (t value in values)   {     double result;     if (tryconverttodouble((object)value, out result))     {       consumevalue(result);     }   } }  public void consumevalue(double value) { } 

the problem above code casting object, results in boxing in loop.

is there way achieve same functionality, i.e. feeding consumevalue values without resorting boxing in foreach loop? note, f must generic method.

i can live expensive preparation code long executed outside loop once. instance, if fancy dynamic method needs emitted, fine if done once.

edit

t guaranteed of numeric type or bool.

motivation. imagine meta data driven application, agent reports data stream, data item type dynamically emitted based on data stream meta data. imagine also, there normalizer engine, knows normalize numeric data streams according algorithm. type of incoming numeric data stream known @ run time , can directed generic method of data type. normalizer, however, expects doubles , produces doubles. high level description, please not delve it.

edit2

concerning cast double. have method convert double following signature:

bool tryconverttodouble(object value, out double result); 

i should have used in example in first place, wanted save space , written not going work. fixed now. noting.

edit3

guys, current implementation box values. , if not have profiler's verdict performance penalty of (if any), still interesting know whether there solution without boxing (and without converting string). let me call purely academic interest. interests me, because things trivial in c++ templates, but, of course, not starting yet stupid , pointless argument on better .net generics or c++ templates. please, ignore last sentence.

edit4

thanks https://stackoverflow.com/users/267/lasse-v-karlsen provided answer. actually, have used code sample write simple class this:

public static class utils<t> {   private static class todoubleconverterholder   {     internal static func<t, double> value = emitconverter();      private static func<t, double> emitconverter()     {       throwifnotconvertabletodouble(typeof(t));        var method = new dynamicmethod(string.empty, typeof(double), typearray<t>.value);       var il = method.getilgenerator();        il.emit(opcodes.ldarg_0);       if (typeof(t) != typeof(double))       {         il.emit(opcodes.conv_r8);       }       il.emit(opcodes.ret);        return (func<t, double>)method.createdelegate(typeof(func<t, double>));     }   }    public static double converttodouble(t value)   {     return todoubleconverterholder.value(value);   } } 

where:

  • throwifnotconvertabletodouble(type) simple method makes sure given type can converted double, i.e. numeric type or bool.
  • typearray helper class produce new[]{ typeof(t) }

the utils.converttodouble method converts numeric value double in efficient way, shown answer question.

it works charm - man.

note: there bug in initial code instance-based code generation. please re-check code below. changed part order of loading values onto stack (ie. .emit lines). both code in answer , repository has been fixed.

if want go route of code generation, hint in question, here's sample code:

it executes consumevalue (which nothing in example) 10 million times, on array of ints , array of booleans, timing execution (it runs code once, remove jit overhead skewing timing.)

the output:

f1 ints = 445ms         <-- uses convert.todouble f1 bools = 351ms f2 ints = 159ms         <-- generates code on each call f2 bools = 167ms f3 ints = 158ms         <-- caches generated code between calls f3 bools = 163ms 

roughly 65% less overhead code generation.

the code available mercurial repository here: http://hg.vkarlsen.no/hgweb.cgi/stackoverflow, browse finding question number.

the code:

using system; using system.collections.generic; using system.diagnostics; using system.linq; using system.reflection; using system.reflection.emit;  namespace consoleapplication15 {     class program     {         public static void f1<t>(ilist<t> values) t : struct         {             foreach (t value in values)                 consumevalue(convert.todouble(value));         }          public static action<t> generateaction<t>()         {             dynamicmethod method = new dynamicmethod(                 "action", methodattributes.public | methodattributes.static,                 callingconventions.standard,                 typeof(void), new type[] { typeof(t) }, typeof(program).module,                 false);             ilgenerator il = method.getilgenerator();              il.emit(opcodes.ldarg_0); // value passed action             il.emit(opcodes.conv_r8);             il.emit(opcodes.call, typeof(program).getmethod("consumevalue"));             il.emit(opcodes.ret);              return (action<t>)method.createdelegate(typeof(action<t>));         }          public static void f2<t>(ilist<t> values) t : struct         {             action<t> action = generateaction<t>();             foreach (t value in values)                 action(value);         }          private static dictionary<type, object> _actions =             new dictionary<type, object>();         public static void f3<t>(ilist<t> values) t : struct         {             object actionobject;             if (!_actions.trygetvalue(typeof(t), out actionobject))             {                 actionobject = generateaction<t>();                 _actions[typeof (t)] = actionobject;             }             action<t> action = (action<t>)actionobject;             foreach (t value in values)                 action(value);         }          public static void consumevalue(double value)         {         }          static void main(string[] args)         {             stopwatch sw = new stopwatch();              int[] ints = enumerable.range(1, 10000000).toarray();             bool[] bools = ints.select(i => % 2 == 0).toarray();              (int pass = 1; pass <= 2; pass++)             {                 sw.reset();                 sw.start();                 f1(ints);                 sw.stop();                 if (pass == 2)                     console.out.writeline("f1 ints = "                         + sw.elapsedmilliseconds + "ms");                  sw.reset();                 sw.start();                 f1(bools);                 sw.stop();                 if (pass == 2)                     console.out.writeline("f1 bools = "                         + sw.elapsedmilliseconds + "ms");                  sw.reset();                 sw.start();                 f2(ints);                 sw.stop();                 if (pass == 2)                     console.out.writeline("f2 ints = "                         + sw.elapsedmilliseconds + "ms");                  sw.reset();                 sw.start();                 f2(bools);                 sw.stop();                 if (pass == 2)                     console.out.writeline("f2 bools = "                         + sw.elapsedmilliseconds + "ms");                  sw.reset();                 sw.start();                 f3(ints);                 sw.stop();                 if (pass == 2)                     console.out.writeline("f3 ints = "                         + sw.elapsedmilliseconds + "ms");                  sw.reset();                 sw.start();                 f3(bools);                 sw.stop();                 if (pass == 2)                     console.out.writeline("f3 bools = "                         + sw.elapsedmilliseconds + "ms");             }         }     } } 

note if make generationaction, f2/3 , consumevalue non-static, have change code slightly:

  1. all action<t> declarations becomes action<program, t>
  2. change creation of dynamicmethod include "this" parameter:

    dynamicmethod method = new dynamicmethod(     "action", methodattributes.public | methodattributes.static,     callingconventions.standard,     typeof(void), new type[] { typeof(program), typeof(t) },     typeof(program).module,     false); 
  3. change instructions load right values @ right times:

    il.emit(opcodes.ldarg_0); // "this" il.emit(opcodes.ldarg_1); // value passed action il.emit(opcodes.conv_r8); il.emit(opcodes.call, typeof(program).getmethod("consumevalue")); il.emit(opcodes.ret); 
  4. pass "this" action whenever called:

    action(this, value); 

here's complete changed program non-static methods:

using system; using system.collections.generic; using system.diagnostics; using system.linq; using system.reflection; using system.reflection.emit;  namespace consoleapplication15 {     class program     {         public void f1<t>(ilist<t> values) t : struct         {             foreach (t value in values)                 consumevalue(convert.todouble(value));         }          public action<program, t> generateaction<t>()         {             dynamicmethod method = new dynamicmethod(                 "action", methodattributes.public | methodattributes.static,                 callingconventions.standard,                 typeof(void), new type[] { typeof(program), typeof(t) },                 typeof(program).module,                 false);             ilgenerator il = method.getilgenerator();              il.emit(opcodes.ldarg_0); // "this"             il.emit(opcodes.ldarg_1); // value passed action             il.emit(opcodes.conv_r8);             il.emit(opcodes.call, typeof(program).getmethod("consumevalue"));             il.emit(opcodes.ret);              return (action<program, t>)method.createdelegate(                 typeof(action<program, t>));         }          public void f2<t>(ilist<t> values) t : struct         {             action<program, t> action = generateaction<t>();             foreach (t value in values)                 action(this, value);         }          private static dictionary<type, object> _actions =             new dictionary<type, object>();         public void f3<t>(ilist<t> values) t : struct         {             object actionobject;             if (!_actions.trygetvalue(typeof(t), out actionobject))             {                 actionobject = generateaction<t>();                 _actions[typeof (t)] = actionobject;             }             action<program, t> action = (action<program, t>)actionobject;             foreach (t value in values)                 action(this, value);         }          public void consumevalue(double value)         {         }          static void main(string[] args)         {             stopwatch sw = new stopwatch();              program p = new program();             int[] ints = enumerable.range(1, 10000000).toarray();             bool[] bools = ints.select(i => % 2 == 0).toarray();              (int pass = 1; pass <= 2; pass++)             {                 sw.reset();                 sw.start();                 p.f1(ints);                 sw.stop();                 if (pass == 2)                     console.out.writeline("f1 ints = "                         + sw.elapsedmilliseconds + "ms");                  sw.reset();                 sw.start();                 p.f1(bools);                 sw.stop();                 if (pass == 2)                     console.out.writeline("f1 bools = "                         + sw.elapsedmilliseconds + "ms");                  sw.reset();                 sw.start();                 p.f2(ints);                 sw.stop();                 if (pass == 2)                     console.out.writeline("f2 ints = "                         + sw.elapsedmilliseconds + "ms");                  sw.reset();                 sw.start();                 p.f2(bools);                 sw.stop();                 if (pass == 2)                     console.out.writeline("f2 bools = "                         + sw.elapsedmilliseconds + "ms");                  sw.reset();                 sw.start();                 p.f3(ints);                 sw.stop();                 if (pass == 2)                     console.out.writeline("f3 ints = "                         + sw.elapsedmilliseconds + "ms");                  sw.reset();                 sw.start();                 p.f3(bools);                 sw.stop();                 if (pass == 2)                     console.out.writeline("f3 bools = "                         + sw.elapsedmilliseconds + "ms");             }         }     } } 

Comments

Popular posts from this blog

c++ - How do I get a multi line tooltip in MFC -

asp.net - In javascript how to find the height and width -

c# - DataTable to EnumerableRowCollection -