2010-12-11 14 views
7

Utilizzo il toolkit esteso WPF (http://wpftoolkit.codeplex.com/).C# Generics: come utilizzare x.MaxValue/x.MinValue (int, float, double) in una classe generica

Ha un bel controllo NumericUpDown che vorrei usare, ma internamente usa il doppio - il che significa che usa double.MinValue e double.MaxValue.

Mi piacerebbe utilizzare lo stesso controllo, ma ho bisogno di una versione generica - per int è necessario utilizzare int.MaxValue/MinValue, per float float.MaxValue/MinValue, ecc. (Penso che tu abbia l'idea :))

Quindi ho pensato di copiare NumericUpDown su GNumericUpDown, dove T sarebbe naturalmente il Type .. Ma questo non funziona, perché un Tipo generico non ha MinValue/MaxValue. E normalmente userei la clausola 'where' per specificare un tipo di base, ma questo non funziona come afaik non c'è un'interfaccia comune che definisce 'MinValue' e 'MaxValue'.

C'è un modo per risolvere questo con generici, o ho davvero bisogno di copiare/incollare/cercare & sostituire il NumericUpDown originale per ogni tipo?

+1

La tua più grande preoccupazione sarà come fare l'aritmetica con un tipo generico ... vedi [il lavoro di Marc Gravell con Expression Trees per quello] (http://www.yoda.arachsys.com/csharp/genericoperators. html). –

risposta

9

L'OP ha fatto questo commento su un'altra risposta:

voglio usare questi controlli nel mio XAML. La mia idea era quella di creare un generico versione , e quindi creare classi vuote come NumericUpDownInt: GNumericUpDown {} Vorrei che essere la strada da percorrere, o c'è un modo migliore/più pulito alla vostra conoscenza

Se avete intenzione di seguire questa strada, poi basta passare il min e max direttamente:

class abstract GenericNumericUpDown<T> 
{ 
    public GenericNumericUpDown(T min, T max) { ... } 
} 

class NumericUpDownInt : GenericNumericUpDown<int> 
{ 
    public NumericUpDownInt() : base(int.MinValue, int.MaxValue) { ... } 
} 

class NumericUpDownFloat : GenericNumericUpDown<float> 
{ 
    public NumericUpDownFloat() : base(float.MinValue, float.MaxValue) { ... } 
} 

class NumericUpDownDouble : GenericNumericUpDown<double> 
{ 
    public NumericUpDownDouble() : base(double.MinValue, double.MaxValue) { ... } 
} 
+0

Beh, naturalmente. Sono scioccato dal motivo per cui non mi è venuto in mente. Immagino che sia uno di quei casi di pensiero troppo complessi, quindi ho perso la soluzione semplice. – Pygmy

9

Beh, dato che si possono ottenere presso il tipo in fase di esecuzione, si potrebbe fare affidamento sul fatto che tutti i tipi numerici in .NET hanno MinValue e MaxValue campi, e leggerli con la riflessione. Non sarebbe terribilmente bello, ma abbastanza facile da fare:

using System; 
using System.Reflection; 

// Use constraints which at least make it *slightly* hard to use 
// with the wrong types... 
public class NumericUpDown<T> where T : struct, 
    IComparable<T>, IEquatable<T>, IConvertible 
{ 
    public static readonly T MaxValue = ReadStaticField("MaxValue"); 
    public static readonly T MinValue = ReadStaticField("MinValue"); 

    private static T ReadStaticField(string name) 
    { 
     FieldInfo field = typeof(T).GetField(name, 
      BindingFlags.Public | BindingFlags.Static); 
     if (field == null) 
     { 
      // There's no TypeArgumentException, unfortunately. You might want 
      // to create one :) 
      throw new InvalidOperationException 
       ("Invalid type argument for NumericUpDown<T>: " + 
       typeof(T).Name); 
     } 
     return (T) field.GetValue(null); 
    } 
} 

class Test 
{ 
    static void Main() 
    { 
     Console.WriteLine(NumericUpDown<int>.MaxValue); 
     Console.WriteLine(NumericUpDown<float>.MinValue); 
    } 
} 

Si noti che se si utilizza questo con un tipo inadeguato, ho cercato di forzare un errore di compilazione nel miglior modo possibile ... ma non sarà infallibile. Se riesci a trovare una struttura con tutte le interfacce giuste ma senza i campi MinValue e MaxValue, qualsiasi tentativo di utilizzare lo NumericUpDown con quel tipo causerà il lancio di un'eccezione.

+0

Il campione sarebbe estremamente apprezzato! :) A proposito, Jon, a giudicare dal tuo tempo di risposta, sei sovrumano per caso? Prima di tutto sembri sapere forse non tutto, ma qualcosa di simile - e tu sembri sempre presente quando qualcuno come me è nei guai :) Saluti :) – Pygmy

+0

@IUsedToBeAPygmy: eri solo fortunato :) Penso che io sto per andare fuori rete per la notte ... –

+0

Jon - prima di tutto, grazie per l'input geniale - non l'ho ancora testato, ma anche se non fosse compilato mi hai dato una direzione completamente nuova a cui pensare :) Ma proprio mentre ho la tua attenzione;) - Voglio usare questi controlli nel mio XAML. La mia idea era quella di creare una versione generica e quindi creare classi vuote come NumericUpDownInt: GNumericUpDown {} Questa sarebbe la strada da percorrere, o c'è un modo migliore/più pulito per le tue conoscenze? – Pygmy

2

si dovrebbe ottenere il codice sorgente più recente per l'esteso Toolkit WPF. Il controllo NumericUpDown aggiornato ti consente di specificare quale tipo di dati usare nell'editor. Il seguente codice specifica di utilizzare un Int32 come tipo di dati anziché il doppio predefinito. Come puoi vedere, ciò avviene impostando la proprietà ValueType sul controllo NumericUpDown.

<extToolkit:NumericUpDown Grid.Row="1" Value="{Binding Age}" Increment="1" Minimum="18" Maximum="65" ValueType="{x:Type sys:Int32}" /> 
0

come questo è utile anche in scenari di test:

È possibile utilizzare metodi di estensione, tale che in C# 6 (introducendo nameof) si potrebbe scrivere:

public static class TypeExtension 
{ 
    public static T MinValue<T>(this Type self) 
    { 
     return (T)self.GetField(nameof(MinValue)).GetRawConstantValue(); 
    } 

    public static T MaxValue<T>(this Type self) 
    { 
     return (T)self.GetField(nameof(MaxValue)).GetRawConstantValue(); 
    } 

} 

e invocare attraverso una sintassi certamente brutta:

var intMinValue = typeof(int).MinValue<int>(); 

che nel vostro metodo generico wo ULD essere

var typeMinValue = typeof(T).MinValue<T>(); 

nota, che non sono certo che tutti i tipi primitivi dichiarano i loro valori Min/Max come costanti. Utilizzare invece GetValue.