Best Practice : Gleiche operationen fuer verschiedene Datentypen?

  • C#
  • .NET 4.5

Es gibt 29 Antworten in diesem Thema. Der letzte Beitrag () ist von exc-jdbi.

    @φConst:
    der Code ergibt für mich irgendwie gar keinen Sinn? typoef(T) und type wird in der schleife immer gleich sein, warum dann nicht direkt typeof(T) verwenden? Das nächste ist das T natürlich bereits von typeof(T) ist, warum dann dazu casten? Das zurück casten ergibt zumindest mit byte Sinn^^
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---
    Eben ja. Das meinte ich ja mit "Ich weiß nicht was ich mir damals gedacht hab".
    DotNETWork (Generische Tcp-Klasse, verschlüsselt!)
    MonogameMinecraftClone (Minecraft-Klon)
    NeuroEvolution (Implementation zweier Lernmethoden für neuronale Netze)
    @ErfinderDesRades
    Stell Dir vor, Du würdest die LINQ-Erweiterungsmethode <Extension()> Function Sum(IEnumerable(Of T)) As T implementieren. Die Funktion soll natürlich für alle gängigen Zahlentypen (Integer in allen Größen, mit und ohne Vorzeichen, Gleitkommazahlen in allen Größen, eventuell auch Decimal) funktionieren. Wie machst Du das?
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    Hier eine kleine Idee von mir

    C#-Quellcode

    1. //Rextester.Program.Main is the entry point for your code. Don't change it.
    2. //Compiler version 4.0.30319.17929 for Microsoft (R) .NET Framework 4.5
    3. using System;
    4. using System.Collections.Generic;
    5. using System.Linq;
    6. using System.Text.RegularExpressions;
    7. using System.Reflection;
    8. using System.Linq.Expressions;
    9. using Microsoft.CSharp.RuntimeBinder;
    10. namespace Rextester
    11. {
    12. class Arithmetic<T>
    13. {
    14. public static readonly Func<T,T,T> Add = GenerateAdd();
    15. public static readonly Func<T,T,T> Subtract = GenerateSubtract();
    16. public static MethodInfo GetOperator(string name)
    17. {
    18. if (typeof(T).IsPrimitive)
    19. return null;
    20. var m = typeof(T).GetMethod("op_" + name, BindingFlags.Public | BindingFlags.Static);
    21. return m;
    22. }
    23. public static Func<T,T,T> GenerateBinaryOperation(Func<Expression,Expression,BinaryExpression> generateBody)
    24. {
    25. var param1 = Expression.Parameter(typeof(T), "val1");//theoretisch könnte man dies hier sogar generisch machen, da in IL "add" auf generischen typen geht(jedoch nur mit primitiven typen funktioniert)...
    26. var param2 = Expression.Parameter(typeof(T), "val2");
    27. bool needUpCast = typeof(T) == typeof(byte);// Spezialfall, da byte die arithmetischen operationen nicht unterstützt...
    28. Expression paramValue1 = param1;
    29. Expression paramValue2 = param2;
    30. if (needUpCast)
    31. {
    32. paramValue1 = Expression.Convert(paramValue1,typeof(int));//cast byte to int
    33. paramValue2 = Expression.Convert(paramValue2,typeof(int));//cast byte to int
    34. }
    35. Expression body = generateBody(paramValue1, paramValue2);
    36. if (needUpCast)
    37. {
    38. body = Expression.Convert(body,typeof(T)); // cast result back to byte
    39. }
    40. return Expression.Lambda<Func<T, T, T>>(body, param1, param2).Compile();
    41. }
    42. public static Func<T,T,T> GenerateAdd()
    43. {
    44. return GenerateBinaryOperation((param1,param2) => Expression.Add(param1, param2, GetOperator("Addition")));
    45. }
    46. public static Func<T,T,T> GenerateSubtract()
    47. {
    48. return GenerateBinaryOperation((param1,param2) => Expression.Subtract(param1, param2, GetOperator("Subtraction")));
    49. }
    50. }
    51. class Arithmetic//für aufrufen ohne angabe von parametertyp
    52. {
    53. public static T Add<T>(T val1, T val2)
    54. {
    55. return Arithmetic<T>.Add(val1, val2);
    56. }
    57. public static T Subtract<T>(T val1, T val2)
    58. {
    59. return Arithmetic<T>.Subtract(val1, val2);
    60. }
    61. }
    62. struct TestStruct
    63. {
    64. public TestStruct(int val)
    65. {
    66. Val = val;
    67. }
    68. public int Val;
    69. public static TestStruct operator +(TestStruct val1,TestStruct val2)
    70. {
    71. return (val1.Val + val2.Val) + 2;
    72. }
    73. public static TestStruct operator -(TestStruct val1,TestStruct val2)
    74. {
    75. return (val1.Val - val2.Val) + 2;
    76. }
    77. public static implicit operator TestStruct(int d)
    78. {
    79. return new TestStruct(d);
    80. }
    81. public override string ToString()
    82. {
    83. return Val.ToString();
    84. }
    85. }
    86. public class Program
    87. {
    88. public static void Test1()
    89. {
    90. TestStruct a = 10;
    91. var b = 12;
    92. var c = Arithmetic.Add(a,b);
    93. Console.WriteLine(c);
    94. }
    95. public static void Test2()
    96. {
    97. byte a = 10;
    98. byte b = 12;
    99. var c = Arithmetic.Add(a,b);
    100. Console.WriteLine(c);
    101. }
    102. public static void Test3()
    103. {
    104. byte a = 10;
    105. float b = 12.2f;
    106. var c = Arithmetic.Subtract(a,b);
    107. Console.WriteLine(c);
    108. }
    109. public static void Main(string[] args)
    110. {
    111. Test1();
    112. Test2();
    113. Test3();
    114. }
    115. }
    116. }


    Ist sogar getestet auf rextester.com sonst hätte ich das so nicht hinbekommen...

    Die Idee dahinter ist, dass man dynamisch Code erzeugt und den erzeugten Code cached. Durch readonly kann .Net sogar einen direkt verweis auf den zugrundelegenden Delegaten machen und spart somit eine Kleinigkeit.
    Nachteil dieser Methode ist sie können nicht geinlined werden und brauchen pro verwendetem Typ eine kleine Vorlaufzeit um es das erste mal zu kompilieren.
    Danach sollte es annähernd so schnell sein wie wenn man direkt die normalen Operationen aufruft(bis auf das inlining halt eben)...
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---

    exc-jdbi schrieb:

    Der konventionelle Weg mit Generica


    Wobei die eigentlich Frage vom TE war ja:

    Rikudo schrieb:

    Wie kann ich verhindern das ich bspw viele Overloads der Rechenmethoden für jeden Datentyp benötige?
    also geht diese Lösung voll an der Frage vorbei (?), aber das ist trotzdem hilfreich für andere. :thumbup:
    @Bluespide

    Ein bisschen geht's schon an der Frage vorbei.

    Da ich noch mit FW4.0 arbeite, hab ich mir das nochmals zu gemüte genommen, um zu schauen, ob es trotzdem auf dem "normalen" Weg was gibt. Gefunden habe ich nichts dergleichen. Weiss auch nicht, ob in den neueren Versionen sowas schon möglich ist. Ich hoffe es.

    Sofern es doch eine Möglichikeit gibt (nebst die von @jvbsl vorgeschlagen) in FW4.0, bitte melden. Danke.

    Freundliche Grüsse

    exc-jdbi