2013-06-10 16 views
6

Zastanawiam się, czy istnieje sposób, w języku C#, mieć typ oparty na typie pierwotnym i powielać go, aby zamiast tego używać innych prymitywów.Dyskusja na temat ogólnego typu numerycznego w C#

Wiem, że to niezbyt jasne, naprawdę nie wiem jak to wyjaśnić (a angielski nie jest moim ojczystym językiem, przepraszam za to ...), więc spróbuję wyjaśnić to za pomocą pseudo- kod.

Szybki przykład:

public struct Vector2d { 
    public double X; 
    public double Y; 

    //Arithmetic methods: Length, normalize, rotate, operators overloads, etc... 
} 

Problemem jest to, że chciałbym mieć pływak oraz wersję Int32 tego typu.

co robię faktycznie (kod ważny C#):

//This type contains all useful methods 
public struct Vector2d { 
    public float X; 
    public float Y; 

    public Vector2f AsFloat() { 
     return new Vector2f((float)X, (float)Y); 
    } 
    public Vector2f AsInteger() { 
     return new Vector2i((int)X, (int)Y); 
    } 

    //Arithmetic methods 
} 

//These types are only here to be casted from/to, all the arithmetics methods are on the double-based type 
public struct Vector2f { 
    public float X; 
    public float Y; 

    public Vector2f AsDouble() { 
     return new Vector2d(X, Y); 
    } 
    public Vector2f AsInteger() { 
     return new Vector2i((int)X, (int)Y); 
    } 
} 
public struct Vector2i { 
    public int X; 
    public int Y; 

    public Vector2f AsFloat() { 
     return new Vector2f(X, Y); 
    } 
    public Vector2f AsDouble() { 
     return new Vector2d(X, Y); 
    } 
} 

To działa, ale to nie jest „sexy”, a w przypadku rozległych odlewów z zastosowaniem metody AsXXX, nie będzie nieuchronnie wpływ na występy.

Idealnie, chciałbym mieć arytmetyki methodes na wszystkich trzech typach, ale byłoby to ból do utrzymania ...

Co byłoby idealne rozwiązanie (pseudo-code, nie ważne C#):

public struct Vector2<T> where T : numeric { 
    public T X; 
    public T Y; 

    public T Length { 
     return (T)Math.Sqrt(X * X + Y * Y); 
    } 
    //Other arithmetic methods 
} 

wiem, że to nie jest możliwe w języku C# na chwilę, ale tutaj jest prawdziwe pytanie:

Czy masz jakiś pomysł, jak ładnie i sprawnie sobie z tym poradzić?

Co myślałem do (pseudo-code, nie ważne C#): Kod

//The TYPE defined constant should be used by Vector2Body instead of plain typed "double", "float", etc... 

public struct Vector2d { 
    #define TYPE double 
    #import Vector2Body 
} 

public struct Vector2f { 
    #define TYPE float 
    #import Vector2Body 
} 

public struct Vector2i { 
    #define TYPE int 
    #import Vector2Body 
} 

ten sposób bym nie powtarzają, łatwiejsze do utrzymania

czeka na swoje pomysły :)

PS: Jeśli moderator mają pomysły na lepsze formatować moje pytanie, nie krępuj się go :) edytować

+1

myślę 'Vector2 gdzie T: IConvertible' – Jodrell

+0

Istnieją sposoby, wystarczy wyszukać "[C#] [generycznych] Numery", a otrzymasz wiele wyników. Ale będzie to miało poważny wpływ na wydajność. A biorąc pod uwagę, że chcesz pracować z wektorami, tutaj najważniejsza jest wydajność. – Euphoric

+1

możliwe duplikat [C#: ogólnego interfejsu dla numerów] (http://stackoverflow.com/questions/1325131/c-generic-interface-for-numbers) – Euphoric

Odpowiedz

5

Tutaj jest bardzo miłe podejście podane przez @ mike-Z, z użyciem szablonów T4:

szablon (plik .tt):

<#@ template debug="false" hostspecific="false" language="C#" #> 
<#@ assembly name="System.Core" #> 
<#@ import namespace="System.Linq" #> 
<#@ import namespace="System.Text" #> 
<#@ import namespace="System.Collections.Generic" #> 
<#@ output extension=".cs" #> 
using System; 

namespace My.Math { 
<# Vector2Body("d", "double"); #> 
<# Vector2Body("f", "float"); #> 
<# Vector2Body("i", "int"); #> 
} 

<#+ private void Vector2Body(string suffix, string t) { #> 

    public struct Vector2<#= suffix #> { 
     public <#= t #> X; 
     public <#= t #> Y; 

     //Arithmetic for <#= t #>... 
    } 

<#+ } #> 

A auto wygenerowany kod:

using System; 

namespace My.Math { 

    public struct Vector2d { 
     public double X; 
     public double Y; 

     //Arithmetic for double... 
    } 


    public struct Vector2f { 
     public float X; 
     public float Y; 

     //Arithmetic for float... 
    } 


    public struct Vector2i { 
     public int X; 
     public int Y; 

     //Arithmetic for int... 
    } 

} 

Nie ma generycznych, więc nie ma wpływu na wydajność, kod napisany raz ...

+0

Teraz jedyną małą wadą tej metody jest brak podświetlania syntaksatora podczas pracy z plikiem .tt, ale ponieważ .cs jest generowany automatycznie za każdym razem, gdy zapisujesz plik .tt, a plik .cs, ma podświetlanie synthax ... – ingham

+2

Jest to zgodne z tym, o czym myślałem. Dodam tylko, że możesz chcieć zajrzeć do częściowych klas (lub struktur tutaj) i częściowych metod, jeśli chcesz w ogóle dostosować wygenerowany kod. –

4

można zrobić coś takiego,

public struct Vector2<T> where T : IConvertible 
{ 
    private readonly double x; 
    private readonly double y; 

    public Vector2(T x, T y) 
    { 
     this.x = x.ToDouble(CultureInfo.CurrentCulture); 
     this.y = y.ToDouble(CultureInfo.CurrentCulture); 
    } 

    public T X 
    { 
     get 
     { 
      return ConvertBack(this.x); 
     } 
    } 

    public T Y 
    { 
     get 
     { 
      return ConvertBack(this.y); 
     } 
    } 

    private static T ConvertBack(double d) 
    { 
     return (t)Convert.ChangeType(d, typeof(T), CultureInfo.CurrentCulture); 
    } 
} 

, ale jeśli nie chcesz ogólnego leczenia. Wtedy równie dobrze możesz mieć kilka wyspecjalizowanych typów Vector2.

+0

Bardzo interesujące podejście. Jaki byłby wpływ wydajności na intensywne użytkowanie? – ingham

+0

@ingham konwersje muszą spowolnić. Generics w ogóle nie mają wydajności. – Jodrell

0

Myślałem o tym samym rozwiązaniu, co sugerował Jodrell. tylko dodać:

private Vector2(double x, double y) 
{ 
this.x = x; 
this.y = y; 
} 

public Vector2<E> ToVector<E>() where E : IConvertible 
{ 
return new Vector2<E>(x, y); 
} 
Powiązane problemy