简体   繁体   English

如何在C#中检查(通用)数字类型是否为整数或非整数类型?

[英]How to check whether a (generic) number type is an integral or nonintegral type in C#?

I've got a generic type T . 我有一个通用类型T Using Marc's Operator class I can perform calculations on it. 使用Marc的Operator我可以对它进行计算。

Is it possible to detect by mere calculations whether the type is an integral or a nonintegral type? 是否可以通过计算来检测类型是整数还是 整数

Perhaps there is a better solution? 也许有更好的解决方案? I'd prefer to support any possible type, so I'd like to prevent hard-coding which types are integral/nonintegral. 我更愿意支持任何可能的类型,所以我想防止硬编码哪些类型是整数/非整数。

Background info 背景信息

The situation I find myself in is I want to cast a double to T but round to the nearest value of T to the double value. 我发现自己的情况是我想将一个double精度投射到T但是将圆的最小值T舍入到double值。

int a = (int)2.6 results in 2 while I want it to result it in 3 , without knowing the type (in this case int ). int a = (int)2.6得到2而我希望它在3它,而不知道类型(在本例中为int )。 It could also be double , in which case I want the outcome to be 2.6 . 它也可能是double ,在这种情况下,我希望结果为2.6

Have you tried Convert.ChangeType ? 你试过Convert.ChangeType吗? Something like: 就像是:

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

It will work for all numeric types I think (as long as the first parameter is iConvertible and the type is a supported one which all basic numerics should be I believe). 它适用于我认为的所有数字类型(只要第一个参数是iConvertible,类型是支持的,所有基本数字应该是我相信的)。

Its important to mention that this will call something like double.ToInt32 which rounds values rather than truncates (bankers rounding I believe). 重要的是要提到这将调用像double.ToInt32这样的方法来舍入值而不是截断(我相信银行家四舍五入)。

I tested this in a little LinqPad program and it does what I think you want: 我在一个小的LinqPad程序中测试了它,它做了我认为你想要的:

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

public static T RetNum<T>()
{
    return (T)Convert.ChangeType(1.9d, typeof (T));
}

Here's a method which will determine if a particular value stored in a generic numeric type is an integer without hardcoding. 这是一种方法,它将确定存储在通用数字类型中的特定值是否为没有硬编码的整数。 Tested working for me on .NET 4. Correctly handles all built in numeric types (as defined in the MSDN link at the bottom) except BigInteger , which doesn't implement IConvertible . 测试在.NET上为我工作4.正确处理除BigInteger之外的所有内置数字类型(如底部的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;
        }

Here's a method which will determine whether a particular type is an integral type. 这是一种确定特定类型是否为整数类型的方法。

    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 is the way to convert, with rounding, between two generic numeric types. Convert.ChangeType是通过舍入在两个通用数字类型之间转换的方式。 But for kicks and curiosity, here's a way to convert a generic numeric type to an int , which could be extended to return a generic type without too much difficulty. 但是对于踢腿和好奇心,这里有一种将通用数字类型转换为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
            }
        }
    }

My results: 我的结果:

        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

Additionally, this MSDN page has some example code which might be helpful. 此外, 此MSDN页面有一些示例代码可能会有所帮助。 Specifically, it includes a list of types considered to be numeric. 具体而言,它包括被认为是数字的类型列表。

I'm not 100% sure what you're asking, but: 我不是100%肯定你在问什么,但是:

To check if it's an integral type , use this: if (obj is float || obj is double) , or if typeof(T) == typeof(float) || typeof(T) == typeof(double)) 要检查它是否是整数类型 ,请使用: if (obj is float || obj is double) ,或者if typeof(T) == typeof(float) || typeof(T) == typeof(double)) if typeof(T) == typeof(float) || typeof(T) == typeof(double))

To check if it's an integral value , cast it to a double, and then do if(value == Math.Round(value)) 要检查它是否为整数值 ,请将其if(value == Math.Round(value))转换为double,然后执行if(value == Math.Round(value))

Of course, that is assuming that you have a number in the first place. 当然,假设您首先有一个数字。 I believe that the Operator class you're using supports things like DateTime. 我相信您使用的Operator类支持DateTime之类的东西。 Would it be better to make your generic method have a generic constraint where T : IConvertible ? 是否更好地使您的泛型方法具有通用约束where T : IConvertible That way there'd be explicit ToDouble and ToInteger methods. 这样就有了明确的ToDoubleToInteger方法。

Edit : 编辑

I think I understand: you've got two local variables, double d; T num; 我想我明白了:你有两个局部变量, double d; T num; double d; T num; . You want to cast d to type T , but with proper rounding if T is a integral type. 您希望将d为类型T ,但如果T是整数类型,则使用适当的舍入。 Is that correct? 那是对的吗?

Assuming that's correct, here's what I'd do: 假设这是正确的,这就是我要做的:

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

Chris's answer gives a possible solution to the scenario I mentioned, but for performance reasons I am still attempting to answer the actual question. 克里斯的回答为我提到的场景提供了一个可能的解决方案,但出于性能原因,我仍然试图回答实际问题。

The assumption (untested) is, Convert.ChangeType is much slower than Math.Round() . 假设 (未经测试)是, Convert.ChangeTypeMath.Round()慢得多。 Ideally, I can check one time whether the given type is integral or not, and conditionally call Math.Round() from then on to obtain a much more efficient solution than calling Convert.ChangeType() constantly. 理想情况下,我可以检查一次给定类型是否为整数,并Math.Round()起有条件地调用Math.Round()以获得比不断调用Convert.ChangeType()更有效的解决方案。

I'm attempting the following implementation: 我正在尝试以下实现:

  1. Convert both 3 , 2 and 1 to the desired unknown type. 都转换321至所需的未知类型。 (This assumes a conversion from an int to the numeric type is possible, which should always be possible anyhow.) (这假设可以从int转换为数字类型,无论如何都应该可以。)
  2. In case 3 / 2 == 1 , it is an integral type. 3 / 2 == 1情况下,它是一个整数类型。 Otherwise, it is a nonintegral type. 否则,它是非整数类型。

This solution doesn't rely anywhere on knowing the type and solely uses conversions and calculations. 此解决方案不依赖于知道类型,只使用转换和计算。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM