2010-12-11 14 views
7

Estoy usando el WPF Extended Toolkit (http://wpftoolkit.codeplex.com/).C# Genéricos: cómo usar x.MaxValue/x.MinValue (int, float, double) en una clase genérica

Tiene un buen control NumericUpDown que me gustaría usar, pero internamente usa dobles, lo que significa que usa double.MinValue y double.MaxValue.

Me gustaría usar el mismo control, pero necesito una versión genérica - para las entradas que necesita usar int.MaxValue/MinValue, para flotantes float.MaxValue/MinValue, etc. (Creo que se entiende la idea :))

Así que pensé en copiar el NumericUpDown a un GNumericUpDown, donde T por supuesto sería el Tipo ... Pero esto no funciona, porque un Tipo genérico no tiene MinValue/MaxValue. Y normalmente usaría la cláusula 'where' para especificar un tipo base, pero esto no funciona como afaik, no existe una interfaz común que defina 'MinValue' y 'MaxValue'.

¿Hay una manera de resolver esto con los genéricos, o lo que realmente necesita para copiar/pegar/& búsqueda reemplazan el NumericUpDown original para cada tipo?

+1

Su mayor preocupación va a ser cómo hacer aritmética con un tipo genérico ... ver [el trabajo de Marc Gravell con Expression Trees para eso] (http://www.yoda.arachsys.com/csharp/genericoperators. html). –

Respuesta

9

El PO hizo este comentario en otra respuesta:

quiero utilizar estos controles en mi XAML. Mi idea era crear una versión genérica , y luego crear clases vacías como NumericUpDownInt: GNumericUpDown {} ¿Sería el camino a seguir, o hay una manera mejor/más limpio a su conocimiento

Si vas a ir por ese camino, a continuación, sólo tiene que pasar el mínimo y el máximo directa:

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

Bueno, por supuesto. Estoy sorprendido de por qué esto no se me ocurrió. Me imagino que es uno de esos casos de pensar demasiado complejo, así que me perdí la solución simple. – Pygmy

9

Bueno, considerando que se puede conseguir en el tipo en tiempo de ejecución, que podría confiar en el hecho de que todos los tipos numéricos en .NET tienen MinValue y MaxValue campos, y leerlos con la reflexión. No sería terriblemente agradable, pero es bastante fácil de hacer:

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); 
    } 
} 

Tenga en cuenta que si se utiliza esta otra de un tipo inadecuado, he tratado de forzar un error en tiempo de compilación lo mejor que pueda ... pero no será infalible. Si logra encontrar una estructura con todas las interfaces correctas, pero sin los campos MinValue y MaxValue, cualquier intento de utilizar el NumericUpDown con ese tipo provocará una excepción.

+0

¡La muestra sería extremadamente apreciada! :) Por cierto, Jon, a juzgar por tu respuesta, ¿eres superhumano por casualidad? En primer lugar, pareces saber que tal vez no todo, pero algo cercano a eso, y siempre pareces estar allí cuando alguien como yo está en problemas :) Saludos :) – Pygmy

+0

@IUsedToBeAPygmy: Tuviste suerte :) Creo que ' estoy a punto de salir de la red por la noche ... –

+0

Jon - antes que nada, gracias por la entrada brillante - aún no lo he probado, pero incluso si no compilara me has dado una dirección completamente nueva para pensar :) Pero justo mientras tengo su atención;) - Quiero usar estos controles en mi XAML. Mi idea era crear una versión genérica y luego crear clases vacías como NumericUpDownInt: GNumericUpDown {} ¿Ese sería el camino a seguir, o hay una forma mejor/más limpia para su conocimiento? – Pygmy

2

debe obtener el código fuente más reciente para el Kit de herramientas de WPF extendido. El control actualizado NumericUpDown le permite especificar qué tipo de datos usar en el editor. El siguiente código especifica el uso de un Int32 como tipo de datos en lugar del doble predeterminado. Como puede ver, esto se hace estableciendo la propiedad ValueType en el control NumericUpDown.

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

ya que esto es también útil en escenarios de prueba:

Es posible utilizar métodos de extensión, de manera que en C# 6 (introducción nameof) se podría escribir:

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 invocar a través cierta sintaxis fea:

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

que en su método genérico wo ULD ser

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

Nota, que no estoy seguro de que todos los tipos primitivos declaran sus valores Min/Max como constantes. Use GetValue en su lugar.

Cuestiones relacionadas