Как проверить, является ли (универсальный) тип числа целым или нецелым типом в C #? - PullRequest
10 голосов
/ 15 декабря 2011

У меня есть универсальный тип T. Используя класс Operator Марка , я могу выполнить вычисления на нем.

Можно ли с помощью простых вычислений определить, является ли тип целым или нецелым типом?

Возможно, есть лучшее решение? Я предпочел бы поддерживать любой возможный тип, поэтому я бы хотел предотвратить жесткое кодирование, какие типы являются целыми / нецелыми.

Справочная информация

Ситуация, в которой я нахожусь, заключается в том, что я хочу привести double к T, но округлить до ближайшего значения T до значения double.

int a = (int)2.6 приводит к 2, в то время как я хочу, чтобы оно привело к 3, не зная тип (в данном случае int). Это также может быть double, и в этом случае я хочу, чтобы результат был 2.6.

Ответы [ 4 ]

6 голосов
/ 15 декабря 2011

Вы пробовали Convert.ChangeType ? Что-то вроде:

Convert.ChangeType(1.9d, typeof (T))

Это будет работать для всех числовых типов, которые я думаю (если первый параметр - iConvertible, а тип - поддерживаемый, что, я считаю, должны быть все основные числовые значения).

Важно упомянуть, что это вызовет что-то вроде double.ToInt32, который округляет значения, а не усекает (я думаю, банкиры округляют).

Я проверил это в маленькой программе LinqPad, и она делает то, что, я думаю, вы хотите:

void Main()
{
    var foo = RetNum<decimal>();
    foo.Dump();
}

public static T RetNum<T>()
{
    return (T)Convert.ChangeType(1.9d, typeof (T));
}
3 голосов
/ 15 декабря 2011

Вот метод, который будет определять, является ли конкретное значение, хранящееся в общем числовом типе, целым числом без жесткого кодирования. Проверено, работает для меня на .NET 4. Правильно обрабатывает все встроенные числовые типы (как определено в ссылке MSDN внизу), кроме BigInteger, который не реализует IConvertible.

        public static bool? IsInteger<T>(T testNumber) where T : IConvertible
        {
            // returns null if T is non-numeric
            bool? isInt = null;
            try
            {
                isInt = testNumber.ToUInt64(CultureInfo.InvariantCulture) == testNumber.ToDouble(CultureInfo.InvariantCulture);
            }
            catch (OverflowException)
            {
                // casting a negative int will cause this exception
                try
                {
                    isInt = testNumber.ToInt64(CultureInfo.InvariantCulture) == testNumber.ToDouble(CultureInfo.InvariantCulture);
                }
                catch
                {
                    // throw depending on desired behavior
                }
            }
            catch
            {
                // throw depending on desired behavior
            }
            return isInt;
        }

Вот метод, который определит, является ли конкретный тип целочисленным.

    public static bool? IsIntegerType<T>() where T : IConvertible
    {
        bool? isInt = null;
        try
        {
            isInt = Math.Round((double)Convert.ChangeType((T)Convert.ChangeType(0.1d, typeof(T)),typeof(double)), 1) != .1d;
            // if you don't round it and T is float you'll get the wrong result
        }
        catch
        {   
            // T is a non numeric type, or something went wrong with the activator
        }
        return isInt;
    }

Convert.ChangeType - это способ преобразования с округлением между двумя общими числовыми типами. Но для любопытства вот способ конвертировать универсальный числовой тип в int, который можно расширить, чтобы возвращать универсальный тип без особых затруднений.

    public static int GetInt32<T>(T target) where T : IConvertible
    {
        bool? isInt = IsInteger<T>(target);
        if (isInt == null) throw new ArgumentException(); // put an appropriate message in
        else if (isInt == true)
        {
            try
            {
                int i = target.ToInt32(CultureInfo.InvariantCulture);
                return i;
            }
            catch
            {   // exceeded size of int32
                throw new OverflowException(); // put an appropriate message in
            }
        }
        else
        {
            try
            {
                double d = target.ToDouble(CultureInfo.InvariantCulture);
                return (int)Math.Round(d);
            }
            catch
            {   // exceeded size of int32
                throw new OverflowException(); // put an appropriate message in
            }
        }
    }

Мои результаты:

        double d = 1.9;
        byte b = 1;
        sbyte sb = 1;
        float f = 2.0f;
        short s = 1;
        int i = -3;
        UInt16 ui = 44;
        ulong ul = ulong.MaxValue;
        bool? dd = IsInteger<double>(d); // false
        bool? dt = IsInteger<DateTime>(DateTime.Now); // null
        bool? db = IsInteger<byte>(b); // true
        bool? dsb = IsInteger<sbyte>(sb); // true
        bool? df = IsInteger<float>(f); // true
        bool? ds = IsInteger<short>(s); // true
        bool? di = IsInteger<int>(i); // true
        bool? dui = IsInteger<UInt16>(ui); // true
        bool? dul = IsInteger<ulong>(ul); // true
        int converted = GetInt32<double>(d); // coverted==2
        bool? isd = IsIntegerType<double>(); // false
        bool? isi = IsIntegerType<int>(); // true

Кроме того, на этой странице MSDN приведен пример кода, который может оказаться полезным. В частности, он включает в себя список типов, которые считаются числовыми.

2 голосов
/ 16 декабря 2011

Ответ Криса дает возможное решение упомянутого мной сценария, но по соображениям производительности я все еще пытаюсь ответить на реальный вопрос.

Предположение (не проверено), Convert.ChangeType намного медленнее, чем Math.Round().В идеале я могу один раз проверить, является ли данный тип целочисленным или нет, и с тех пор условно вызвать Math.Round(), чтобы получить гораздо более эффективное решение, чем постоянный вызов Convert.ChangeType().

Я пытаюсьследующая реализация:

  1. Преобразует 3, 2 и 1 в требуемый неизвестный тип.(Предполагается, что преобразование из int в числовой тип возможно, что всегда должно быть возможным в любом случае.)
  2. В случае 3 / 2 == 1 это целочисленный тип.В противном случае это нецелый тип.

Это решение нигде не полагается на знание типа и использует только преобразования и вычисления.

2 голосов
/ 15 декабря 2011

Я не уверен на 100%, что вы спрашиваете, но:

Чтобы проверить, является ли это целым типом , используйте: if (obj is float || obj is double) или if typeof(T) == typeof(float) || typeof(T) == typeof(double))

Чтобы проверить, является ли оно целым значением , приведите его к двойному, а затем выполните if(value == Math.Round(value))

Конечно, это предполагает, что у вас есть номер в первую очередь. Я считаю, что используемый вами класс Operator поддерживает такие вещи, как DateTime. Было бы лучше, чтобы ваш общий метод имел общее ограничение where T : IConvertible? Таким образом, будут явные методы ToDouble и ToInteger.

Редактировать

Мне кажется, я понимаю: у вас есть две локальные переменные, double d; T num;. Вы хотите привести d к типу T, но с правильным округлением, если T является целым типом. Это правильно?

Предполагая, что это правильно, вот что я бы сделал:

public void SomeMethod<T>()
{
    double d;
    // I think I got all the floating-point types. There's only a few, so we can test for them explicitly.
    if(typeof(T) != typeof(double) && typeof(T) != typeof(float) && typeof(T) != typeof(Decimal))
    {
        d = Math.Round(d);
    }
    T converted = Convert.ChangeType(d, typeof(T));
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...