2011-07-01 14 views
5

Próbuję pisać klasy, które obsługują różne typy liczb. Wiem, że C# (i .NET w ogóle, wierzę) ma INumber interfejs, więc nie mogę używać coś jak następuje:Jak mogę napisać pojedynczą klasę, aby kompilować wiele razy z różnymi typami liczb?

public class Adder<T> where T:INumber 
    { 
     public T Add(T a, T b) 
     { 
      return a + b; 
     } 
    } 

To w porządku, chociaż, bo chciałbym uniknąć boks/rozpakowywanie każdej z moich liczb. Mogłem jednak użyć kompilacja warunkowa dla każdego rodzaju chcę wspierać:

#if FLOAT 
    public class AdderF 
    { 
     public float Add(float a, float b) 
#else 
    public class Adder 
    { 
     public int Add(int a, int b) 
#endif 
     { 
      return a + b; 
     } 
    } 

Oznacza to będę musiał skompilować inny Library.dll i LibraryF.dll jednak. Czy jest jakieś bardziej eleganckie rozwiązanie?

Oczywiście w moim przykładzie mogę po prostu napisać kod dwukrotnie. Chciałbym jednak użyć tego procesu, aby stworzyć duże skomplikowane struktury danych z wersją całkowitą i zmiennoprzecinkową, więc nie chcę mieć możliwości kopiowania i wklejania błędów podczas aktualizacji mojej struktury. Nie chcę też, aby utrata prędkości powodowała owijanie struktury zmiennoprzecinkowej w integralnym opakowaniu i niepotrzebne przekształcanie wszystkich danych wejściowych w bardziej łagodny typ danych.

Odpowiedz

3

Sądzę, że to zależy od tego, jak surowo ma być. Czy chcesz użyć metody Add, która pobiera tylko dwa parametry dokładnie tego samego typu (int i int, float i float) ...? Jeśli nie, a wydajność nie jest super ważna, możesz po prostu zaimplementować swoje rozwiązanie za pomocą decimal i wszystko będzie działać.

Jeśli to musi być ścisłe, a chcesz konkretnych wersji dla każdego typu, możesz zajrzeć do generowania kodu T4. Jest podobny do szablonów C++ (niektórzy ludzie uważają, że szablony C++ i generics C# są takie same, ale nie są).

Scott Hanselman napisał o tym an interesting article.

+0

Tak jak powiedziałem, staram się poprawić wydajność moich struktur danych. t Myślę, że użycie 'decymalnej' wszędzie, gdzie mógłbym użyć 'int' zamiast tego byłoby na to skuteczne. T4 to zdecydowanie fajna technologia, o której nie wiedziałem i na pewno się nią zajmie! – dlras2

3

Może to być przydatne: Is there a C# generic constraint for "real number" types?.

Wygląda na to, że nie jest to bezpośrednio możliwe, jak pan powiedział. Przydałoby się to mieć.

+0

Dzięki! Szukałem takiego linku, aby uwzględnić to w moim pytaniu. Wiem, że mogę używać takich kalkulatorów abstrakcyjnych do pracy z różnymi typami liczb, ale moim celem jest zwrócenie większej wydajności z struktur całkowitych, gdy precyzja zmiennoprzecinkowa nie jest konieczna. – dlras2

2

Głównym wyzwaniem jest to, że jitter musi emitować różne kody opcyjne w zależności od tego, czy dodajesz floats, czy int, tak więc, mimo że kod wygląda podobnie, w rzeczywistości jest zupełnie inny od kulis. Jest to możliwe w C++ (ponieważ szablony są kompilowane osobno dla każdej definicji typu), ale generics .NET działa inaczej. Spójrz na T4 templates jako jedno z możliwych rozwiązań (jeśli naprawdę potrzebujesz możliwości kompilacji jako float lub int osobno).

+0

+1 dla wyjaśnienia szablonów C++ w porównaniu do generałów .Net i sugestii T4. – dlras2

+1

Rzeczywiście pominąłem trochę, ponieważ kompilator mógł, prawdopodobnie, obsługiwać ograniczenie "liczby", które pozwalało na emitowanie kodu Add IL OpCode (w przeciwieństwie do próby rozwiązania operatora "add"), przy specyficznym typie, który radzi sobie z różnicami . Będzie to nadal powodować pewne ograniczenia, ponieważ istnieje oddzielna instrukcja Add.Ovf dla liczb całkowitych, więc można obsługiwać tylko dodawanie bez sprawdzania przepełnienia liczby całkowitej.Kompilator potrzebowałby także specjalnego wsparcia dla zrozumienia "prymitywnej liczby", która nie jest jeszcze określonego typu, tworząc zabawne nowe przypadki graniczne dla pana Lipperta. –

Powiązane problemy