2012-04-24 3 views
9

Poniższy kod jest bardzo monotonne:Jak używać rodzajowych rozwijać kod, który działa na deblu i dziesiętnych

public static double Interpolate(double x1, double y1, double x2, double y2, double x) 
    { 
     return y1 + (x - x1) * (y2 - y1)/(x2 - x1); 
    } 
    public static decimal Interpolate(decimal x1, decimal y1, decimal x2, decimal y2, decimal x) 
    { 
     return y1 + (x - x1) * (y2 - y1)/(x2 - x1); 
    } 

Jednak moja próba wykorzystania rodzajowych nie kompiluje:

public static T Interpolate<T>(T x1, T y1, T x2, T y2, T x) 
    { 
     return y1 + (x - x1) * (y2 - y1)/(x2 - x1); 
    } 

błędu komunikat jest następujący:

Błąd 2 Operatora "-" nie można zastosować do argumentów typu "T" i "T" C: \ Git ... \ LinearInterpolator.cs

Jak mogę ponownie użyć mojego kodu?

Edytuj: szybkie środowisko uruchomieniowe jest ważne dla tych modułów.

+3

całkiem pewny, że jesteś bardziej lub mniej zablokowany, co tam masz ... – Servy

+1

jako 'System.Double' i' System.Decimal' nie dzielcie wspólnego przodka określającego operatorów, których potrzebujecie, obawiam się, że nie możecie. –

+0

C# nie ma "generycznych" w powszechnym rozumieniu tego słowa. Nie można użyć niczego z T, chyba że typ jest już znany (ograniczony). –

Odpowiedz

3

Nie można używać operatorów w trybie generycznym bez określenia ograniczenia klasy bazowej na T.

Możesz zrobić coś takiego: Creating a Math library using Generics in C#, ale osobiście, chyba że masz naprawdę wiele różnych formuł, nie widzę sensu.

Istnieje również możliwość dynamicznej kompilacji wyrażenia drzew na T, przepisywanie typy między double/decimal z drzewa „model” generowanego przez kompilator C# (utrzymanie arytmetyczną pierwszeństwo etc) ... jeśli jesteś naprawdę zainteresowany tym, że mogę opublikować kod takiego rozwiązania (będę potrzebował około godziny!). Wydajność środowiska uruchomieniowego będzie wolniejsza.

+0

Dzięki za rozczarowanie. Nie interesują mnie powolne podejścia, wolę raczej żyć z moim powielaniem. –

+0

Nie wiem, że wersja skompilowana dynamicznie byłaby "wolna", ale nieco wolniejsza niż aktualne przeciążenia. Spodziewam się, że będzie to porównywalne z moją 'dynamiczną' wersją. – Gabe

+0

@Gabe masz prawdopodobnie rację (a ja osobiście mam dużo zasobów w dynamicznie kompilowanych i 'dynamicznych' rzeczach, często stwierdzam, że wydajność przekracza moje oczekiwania, przynajmniej po początkowej rozgrzewce) - mógłbym to porównać z Twoje dynamiczne rozwiązanie, jeśli dostanę darmową porcję czasu. Byłoby naprawdę ciekawie zobaczyć :) –

4

Twój obecny kod jest w porządku - prawdopodobnie najlepszy, jaki możesz osiągnąć bez odliczania podwójnych miejsc po przecinku.

Ogólne rozwiązanie to możliwe, ale wymagałoby dużej ilości infrastruktury - tak bardzo, że byłoby to zbyt skomplikowane.

+0

+1 Jestem za unikaniem złożoności. –

4

Ilekroć muszę to zrobić, po prostu uciekam się do dynamic. To zadziała dobrze, ale może być nieco wolniejsze niż przeciążenia, które obecnie masz.

public static T Interpolate<T>(T x1, T y1, T x2, T y2, T x) 
{   
    return y1 + ((dynamic)x - x1) * ((dynamic)y2 - y1)/((dynamic)x2 - x1); 
} 
+1

+1 Ciekawe podejście! –

2

Można użyć IConvertible. Ale różnica w wydajności może być znaczna w zależności od potrzeb. W porównaniu z metodą, która używa Single, różnica może osiągnąć prawie 50% więcej. Doing 100000 iteracji, z jednym trwa 109ms i 156ms przy użyciu Generic.

Zobacz ten kod (.NET 2):

using System; 
using System.Text; 
using NUnit.Framework; 

namespace ProofOfConcept.GenericInterpolation 
{ 
    /// <summary> 
    /// Proof of concept for a generic Interpolate. 
    /// </summary> 
    [TestFixture] 
    public class GenericInterpolationTest 
    { 
     /// <summary> 
     /// Interpolate test. 
     /// </summary> 
     [Test] 
     public void InterpolateTest() 
     { 
      Int16 interpolInt16 = Interpolate<Int16>(2, 4, 5, 6, 7); 
      Int32 interpolInt32 = Interpolate<Int32>(2, 4, 5, 6, 7); 

      Double interpolDouble = Interpolate<Double>(2, 4, 5, 6, 7); 
      Decimal interpolDecimal = Interpolate<Decimal>(2, 4, 5, 6, 7); 

      Assert.AreEqual((Int16)interpolInt32, (Int16)interpolInt16); 
      Assert.AreEqual((Double)interpolDouble, (Double)interpolDecimal); 

      //performance test 
      int qtd = 100000; 
      DateTime beginDt = DateTime.Now; 
      TimeSpan totalTimeTS = TimeSpan.Zero; 
      for (int i = 0; i < qtd; i++) 
      { 
       interpolDouble = Interpolate(2, 4, 5, 6, 7); 
      } 
      totalTimeTS = DateTime.Now.Subtract(beginDt); 
      Console.WriteLine(
       "Non Generic Single, Total time (ms): " + 
       totalTimeTS.TotalMilliseconds); 

      beginDt = DateTime.Now; 
      for (int i = 0; i < qtd; i++) 
      { 
       interpolDouble = Interpolate<Double>(2, 4, 5, 6, 7); 
      } 
      totalTimeTS = DateTime.Now.Subtract(beginDt); 
      Console.WriteLine(
       "Generic, Total time (ms): " + 
       totalTimeTS.TotalMilliseconds); 
     } 

     /// <summary> 
     /// Interpolates the specified x1. 
     /// </summary> 
     /// <typeparam name="T"></typeparam> 
     /// <param name="x1">The x1.</param> 
     /// <param name="y1">The y1.</param> 
     /// <param name="x2">The x2.</param> 
     /// <param name="y2">The y2.</param> 
     /// <param name="x">The x.</param> 
     /// <returns></returns> 
     public static T Interpolate<T>(T x1, T y1, T x2, T y2, T x) where T : IConvertible 
     { 
      IConvertible x1C = x1 as IConvertible; 
      IConvertible y1C = y1 as IConvertible; 
      IConvertible x2C = x2 as IConvertible; 
      IConvertible y2C = y2 as IConvertible; 
      IConvertible xC = x as IConvertible; 
      Decimal retDec = y1C.ToDecimal(null) + 
       (xC.ToDecimal(null) - x1C.ToDecimal(null)) * 
       (y2C.ToDecimal(null) - y1C.ToDecimal(null))/
       (x2C.ToDecimal(null) - x1C.ToDecimal(null)); 

      return (T)((IConvertible)retDec).ToType(typeof(T), null); 
     } 

     /// <summary> 
     /// Interpolates the specified x1. 
     /// </summary> 
     /// <param name="x1">The x1.</param> 
     /// <param name="y1">The y1.</param> 
     /// <param name="x2">The x2.</param> 
     /// <param name="y2">The y2.</param> 
     /// <param name="x">The x.</param> 
     /// <returns></returns> 
     public static Single Interpolate(Single x1, Single y1, Single x2, Single y2, Single x) 
     { 
      Single retSing = y1 + (x - x1) * (y2 - y1)/(x2 - x1); 

      return retSing; 
     } 
    } 
} 
+0

+1 Niektóre bardzo interesujące testy porównawcze! –

Powiązane problemy