2012-02-18 13 views
5

Powiel możliwe:
C# generic constraint for only integersJak sumować liczby ogólne w języku C#?

Jak widać w poniższym kodzie, muszę obliczyć sumę dwóch liczb generycznych.

public class NumberContainer<T> 
{ 
    public T ValueA { get; private set; } 
    public T ValueB { get; private set; } 
    public T Total { get { return ValueA + ValueB; } } 
} 

Jednak nie jest to możliwe do zrobienia bezpośredniego dodawania dwóch wartości t, co powoduje błąd kompilatora poniżej:

Operator „+” nie może być stosowany do argumentów typu „T” i „T”

Biorąc pod uwagę, że nie zamierza korzystać z T na cokolwiek innego niż wartość typów, które reprezentują liczb (krótkie, ushort, int, uint itp), jak mogę wykonać suma? (efektywność jest czynnikiem, który należy wziąć pod uwagę)

+0

ewentualnie: [Czy istnieje C# rodzajowe ograniczenie dla „prawdziwego numeru” typy?] (http://stackoverflow.com/questions/1348594/is-there-ac-sharp-generic-constraint-for-real-number-types/1348625#1348625) –

+1

Pytanie jest zamknięte jako duplikat pytań starając się umieścić ograniczenie na 'T', co jest * nie * tym, co próbuje to pytanie. Myślę, że należy go ponownie otworzyć. – dasblinkenlight

Odpowiedz

12

Można to zrobić z „małej magii” z LINQ:

private static readonly Func<T, T, T> adder; 
static NumberContainer() { 
    var p1 = Expression.Parameter(typeof (T)); 
    var p2 = Expression.Parameter(typeof (T)); 
    adder = (Func<T, T, T>)Expression 
     .Lambda(Expression.Add(p1, p2), p1, p2) 
     .Compile(); 
} 
public T Total { get { return adder(ValueA, ValueB); } } 

Jedyną wadą jest to, że ten kod będzie skompilować nawet jeśli NumberContainer jest tworzony z typem T który nie obsługuje dodawanie; oczywiście rzuci wyjątek w czasie wykonywania. Dodatkową korzyścią jest to, że ten powinien działać pod zdefiniowanymi przez użytkownika operatorami +.

1

Możesz określić ograniczenie dla T, które musi być strukturą, i być może, że implementuje IConvertable, a następnie użyć Convert.

public class NumberContainer<T> 
     where T : struct, IConvertible 
    { 
     public T ValueA { get; private set; } 
     public T ValueB { get; private set; } 
     public T Total { 
      get 
      { 
       // do type checking here, then: 

       return (T)Convert.ChangeType(
        Convert.ToDouble((object)ValueA) + 
        Convert.ToDouble((object)ValueB), typeof(T)); 
      } 
     } 
    } 

Jednak nie sposób z rodzajowych w celu zagwarantowania, że ​​T jest liczbą całkowitą lub pływające typu w czasie kompilacji. Możesz to sprawdzić w środowisku wykonawczym i wygenerować wyjątek.

+0

To też nie działa. W rzeczywistości twoja sugestia nawet się nie kompiluje. –

+0

Zrobiłem to z głowy, naprawiłem i skompilowałem. Jeśli chciałbyś dokładniej wyjaśnić, dlaczego to nie zadziała, byłoby to o wiele bardziej pomocne. – HackedByChinese

+0

DateTime to struct i IConvertible, ale nie można go przekonwertować na podwójne, więc po prostu wyrzuci wyjątek za każdym razem, gdy wywołasz go za pomocą parametrów DateTime. –

5

Oprócz przy użyciu LINQ, jeśli jesteś na .NET 4 jest to możliwe przy użyciu dynamic:

static T Add<T>(T x, T y) 
{ 
    dynamic dx = x, dy = y; 
    return dx + dy; 
} 

W swojej próbki kodu to zamienia się:

public class NumberContainer<T> where T: struct 
{ 
    public T ValueA { get; private set; } 
    public T ValueB { get; private set; } 
    public T Total { get { return ((dynamic)ValueA) + ((dynamic)ValueB); } } 
} 

Podejście to robi” t jednak zapewnić bezpieczeństwo. Jeśli T jest jakąś losową strukturą, która nie obsługuje +, otrzymasz wyjątek (chyba).

+0

Przerażające wykorzystanie dynamiki, zwłaszcza jeśli chodzi o operatorów, nie jest wirtualne ... Myślę, że to "po prostu zadziała" dzięki swojej magicznej skrzynce. (Ja też wolałbym typy strukturalne *: - /) –

+0

@pst - Zgoda, ale zdarza się, że działa. Rozumiem to jednak. –

1

Można traktować to tylko jako abstrakcyjnej klasy bazowej i uzyskania konkretnych klas, które określone wartości T i pod warunkiem wdrożenia dla Total

public abstract class NumberContainer<T> 
    where T: struct 
{ 
    public T ValueA { get; private set; } 
    public T ValueB { get; private set; } 
    public abstract T Total(); 
} 

public class IntContainer : NumberContainer<int> 
{ 
    public override int Total() 
    { 
     return ValueA + ValueB; 
    } 
} 
+1

Jeśli 'int' można przekonwertować na' T', to nie byłoby problemu. Twój kod nie kompiluje się. –

+0

Głupia mnie. Zaktualizowana odpowiedź zapewnia alternatywne rozwiązanie, ponieważ istnieje teraz kilka rozwiązań wykorzystujących dynamikę i łącze –