C# Generics: как использовать x.MaxValue / x.MinValue (int, float, double) в общем классе

Я использую расширенный инструментарий WPF (http://wpftoolkit.codeplex.com/).

У него есть хороший элемент управления NumericUpDown, который я хотел бы использовать, но внутри он использует Double - что означает, что он использует double.Minvalue и двойной.Максвеллову.

Я хотел бы использовать тот же элемент управления, но мне нужна общая версия - для ints он должен использовать int.MaxValue / MinValue, для поплавков поплавок.MaxValue / MinValue, etc. (Я думаю вы поняли :))

Так что я, хотя о копировании NumericUpDown в GNumericUpDown, где T, конечно, будет типом.. Но это не работает, потому что общий тип не имеет MinValue / MaxValue. И обычно я бы использовал предложение "where" для указания базового типа, но это не работает как afaik нет общего интерфейса, который определяет "MinValue" и "MaxValue".

есть ли способ решить эту проблему с помощью дженериков, или мне действительно нужно скопировать/вставить/найти и заменить исходный NumericUpDown для каждого типа ?

4 ответов


OP сделал этот комментарий к другому ответу:

Я хочу использовать эти элементы в моем код XAML. Моя идея заключалась в создании универсального версия, а затем создать пустые классы как NumericUpDownInt : GNumericUpDown { } это будет путь, или есть лучше / чище способ вашего знания

Если вы собираетесь идти по этому маршруту, просто передайте min и max напрямую:

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) { ... }
}

ну, учитывая, что вы можете получить тип во время выполнения, вы мог бы полагайтесь на то, что все числовые типы в .NET имеют MinValue и MaxValue поля, и читать их с отражением. Это было бы не очень приятно, но достаточно легко сделать:

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

обратите внимание, что если вы используете это с неподходящим типом, я попытался заставить ошибку времени компиляции как можно лучше... но он не будет надежным. Если вам удастся найти структуру со всем правом интерфейсы, но без MinValue и MaxValue поля, затем любая попытка использовать NumericUpDown С этим типом вызовет исключение.


вы должны получить последний исходный код для расширенного инструментария WPF. Обновленный элемент управления numericupdown позволяет указать, какой тип данных использовать в Редакторе. Следующий код указывает использовать Int32 в качестве типа данных вместо double по умолчанию. Как вы можете видеть, это делается путем установки свойства ValueType в элементе управления NumericUpDown.

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

а это также полезно в сценариях тестирования:

вы можете использовать методы расширения, например, в C#6 (introducing nameof) вы могли бы написать:

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

}

и вызовите через некоторый, по общему признанию, уродливый синтаксис:

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

который в вашем общем методе будет

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

обратите внимание, что я не уверен, что все примитивные типы объявляют свои значения Min/Max как константы. Использовать .