programing

유형이 숫자인지 확인하는 방법

nasanasas 2020. 9. 4. 07:33
반응형

유형이 숫자인지 확인하는 방법


주어진 .Net 유형이 숫자인지 여부를 확인하는 방법이 있습니까? 예 : System.UInt32/UInt16/Double모두 숫자입니다. .NET에서 긴 스위치 케이스를 피하고 싶습니다 Type.FullName.


이 시도:

Type type = object.GetType();
bool isNumber = (type.IsPrimitiveImple && type != typeof(bool) && type != typeof(char));

기본 형식은 Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Char, Double 및 Single입니다.

촬영 기욤의 솔루션을 조금 더 :

public static bool IsNumericType(this object o)
{   
  switch (Type.GetTypeCode(o.GetType()))
  {
    case TypeCode.Byte:
    case TypeCode.SByte:
    case TypeCode.UInt16:
    case TypeCode.UInt32:
    case TypeCode.UInt64:
    case TypeCode.Int16:
    case TypeCode.Int32:
    case TypeCode.Int64:
    case TypeCode.Decimal:
    case TypeCode.Double:
    case TypeCode.Single:
      return true;
    default:
      return false;
  }
}

용법:

int i = 32;
i.IsNumericType(); // True

string s = "Hello World";
s.IsNumericType(); // False

스위치를 사용하지 말고 세트 만 사용하십시오.

HashSet<Type> NumericTypes = new HashSet<Type>
{
    typeof(decimal), typeof(byte), typeof(sbyte),
    typeof(short), typeof(ushort), ...
};

편집 : 유형 코드를 사용하는 것보다이 방법의 한 가지 장점은 새로운 숫자 유형이 .NET에 도입 될 때 (예 : BigIntegerComplex ) 조정하기 쉽다 는 것입니다. 반면 해당 유형 유형 코드를 얻지 못합니다 .


어떤 솔루션도 Nullable을 고려하지 않습니다.

Jon Skeet의 솔루션을 약간 수정했습니다.

    private static HashSet<Type> NumericTypes = new HashSet<Type>
    {
        typeof(int),
        typeof(uint),
        typeof(double),
        typeof(decimal),
        ...
    };

    internal static bool IsNumericType(Type type)
    {
        return NumericTypes.Contains(type) ||
               NumericTypes.Contains(Nullable.GetUnderlyingType(type));
    }

내 HashSet에 nullables 자체를 추가 할 수 있다는 것을 알고 있습니다. 그러나이 솔루션은 특정 Nullable을 목록에 추가하는 것을 잊어 버릴 위험을 방지합니다.

    private static HashSet<Type> NumericTypes = new HashSet<Type>
    {
        typeof(int),
        typeof(int?),
        ...
    };

public static bool IsNumericType(Type type)
{
  switch (Type.GetTypeCode(type))
  {
    case TypeCode.Byte:
    case TypeCode.SByte:
    case TypeCode.UInt16:
    case TypeCode.UInt32:
    case TypeCode.UInt64:
    case TypeCode.Int16:
    case TypeCode.Int32:
    case TypeCode.Int64:
    case TypeCode.Decimal:
    case TypeCode.Double:
    case TypeCode.Single:
      return true;
    default:
      return false;
  }
}

제거 된 최적화에 대한 참고 사항 (엔지 주석 참조) 그리고 정말로 최적화하고 싶은 경우 (가독성 및 안전성 손실 ...) :

public static bool IsNumericType(Type type)
{
  TypeCode typeCode = Type.GetTypeCode(type);
  //The TypeCode of numerical types are between SByte (5) and Decimal (15).
  return (int)typeCode >= 5 && (int)typeCode <= 15;
}


기본적으로 Skeet의 솔루션이지만 다음과 같이 Nullable 유형과 함께 재사용 할 수 있습니다.

public static class TypeHelper
{
    private static readonly HashSet<Type> NumericTypes = new HashSet<Type>
    {
        typeof(int),  typeof(double),  typeof(decimal),
        typeof(long), typeof(short),   typeof(sbyte),
        typeof(byte), typeof(ulong),   typeof(ushort),  
        typeof(uint), typeof(float)
    };

    public static bool IsNumeric(Type myType)
    {
       return NumericTypes.Contains(Nullable.GetUnderlyingType(myType) ?? myType);
    }
}

유형에 대한 SFun28의 내부 유형 검사통해 강화 된 Philip의 제안 에 기반한 접근 방식 :Nullable

public static class IsNumericType
{
    public static bool IsNumeric(this Type type)
    {
        switch (Type.GetTypeCode(type))
        {
            case TypeCode.Byte:
            case TypeCode.SByte:
            case TypeCode.UInt16:
            case TypeCode.UInt32:
            case TypeCode.UInt64:
            case TypeCode.Int16:
            case TypeCode.Int32:
            case TypeCode.Int64:
            case TypeCode.Decimal:
            case TypeCode.Double:
            case TypeCode.Single:
                return true;
            case TypeCode.Object:
                if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
                {
                    return Nullable.GetUnderlyingType(type).IsNumeric();
                    //return IsNumeric(Nullable.GetUnderlyingType(type));
                }
                return false;
            default:
                return false;
        }
    }
}

왜 이래? 주어진 Type type것이 숫자 유형인지 확인해야하고 임의의 유형이 숫자 인지 확인해야했습니다 object o.


Type.IsPrimitive를 사용 하고 다음 과 같이 BooleanChar유형 을 정렬 할 수 있습니다 .

bool IsNumeric(Type type)
{
    return type.IsPrimitive && type!=typeof(char) && type!=typeof(bool);
}

편집 : 숫자로 간주하지 않으면 IntPtrUIntPtr유형도 제외하고 싶을 수 있습니다 .


C # 7로이 방법에 나에게 스위치 케이스보다 더 나은 성능을 제공 TypeCode하고 HashSet<Type>:

public static bool IsNumeric(this object o) => o is byte || o is sbyte || o is ushort || o is uint || o is ulong || o is short || o is int || o is long || o is float || o is double || o is decimal;

테스트는 다음과 같습니다.

public static class Extensions
{
    public static HashSet<Type> NumericTypes = new HashSet<Type>()
    {
        typeof(byte), typeof(sbyte), typeof(ushort), typeof(uint), typeof(ulong), typeof(short), typeof(int), typeof(long), typeof(decimal), typeof(double), typeof(float)
    };

    public static bool IsNumeric1(this object o) => NumericTypes.Contains(o.GetType());

    public static bool IsNumeric2(this object o) => o is byte || o is sbyte || o is ushort || o is uint || o is ulong || o is short || o is int || o is long || o is decimal || o is double || o is float;

    public static bool IsNumeric3(this object o)
    {
        switch (o)
        {
            case Byte b:
            case SByte sb:
            case UInt16 u16:
            case UInt32 u32:
            case UInt64 u64:
            case Int16 i16:
            case Int32 i32:
            case Int64 i64:
            case Decimal m:
            case Double d:
            case Single f:
                return true;
            default:
                return false;
        }
    }

    public static bool IsNumeric4(this object o)
    {
        switch (Type.GetTypeCode(o.GetType()))
        {
            case TypeCode.Byte:
            case TypeCode.SByte:
            case TypeCode.UInt16:
            case TypeCode.UInt32:
            case TypeCode.UInt64:
            case TypeCode.Int16:
            case TypeCode.Int32:
            case TypeCode.Int64:
            case TypeCode.Decimal:
            case TypeCode.Double:
            case TypeCode.Single:
                return true;
            default:
                return false;
        }
    }
}

class Program
{
    static void Main(string[] args)
    {           
        var count = 100000000;

        //warm up calls
        for (var i = 0; i < count; i++)
        {
            i.IsNumeric1();
        }
        for (var i = 0; i < count; i++)
        {
            i.IsNumeric2();
        }
        for (var i = 0; i < count; i++)
        {
            i.IsNumeric3();
        }
        for (var i = 0; i < count; i++)
        {
            i.IsNumeric4();
        }

        //Tests begin here
        var sw = new Stopwatch();
        sw.Restart();
        for (var i = 0; i < count; i++)
        {
            i.IsNumeric1();
        }
        sw.Stop();

        Debug.WriteLine(sw.ElapsedMilliseconds);

        sw.Restart();
        for (var i = 0; i < count; i++)
        {
            i.IsNumeric2();
        }
        sw.Stop();

        Debug.WriteLine(sw.ElapsedMilliseconds);

        sw.Restart();
        for (var i = 0; i < count; i++)
        {
            i.IsNumeric3();
        }
        sw.Stop();

        Debug.WriteLine(sw.ElapsedMilliseconds);

        sw.Restart();
        for (var i = 0; i < count; i++)
        {
            i.IsNumeric4();
        }
        sw.Stop();

        Debug.WriteLine(sw.ElapsedMilliseconds);
    }

짧은 대답 : 아니요.

더 긴 답변 : 아니요.

사실 C #의 다양한 유형에는 숫자 데이터가 포함될 수 있습니다. 무엇을 예상해야하는지 (Int, Double 등) 모르는 경우 "long"case 문을 사용해야합니다.


이것도 작동 할 수 있습니다. 그러나 Type.Parse로 후속 작업을 수행하여 나중에 원하는 방식으로 캐스팅 할 수 있습니다.

public bool IsNumeric(object value)
{
    float testValue;
    return float.TryParse(value.ToString(), out testValue);
}

불행히도 이러한 유형은 모든 값 유형이라는 점 외에는 공통점이 많지 않습니다. 그러나 긴 전환 사례를 피하기 위해 이러한 모든 유형으로 읽기 전용 목록을 정의한 다음 주어진 유형이 목록 안에 있는지 확인하면됩니다.


모두 값 유형입니다 (boool 및 enum 제외). 따라서 다음을 간단히 사용할 수 있습니다.

bool IsNumberic(object o)
{
    return (o is System.ValueType && !(o is System.Boolean) && !(o is System.Enum))
}

수정 스키트의 및 arviman의 솔루션을 활용 Generics, Reflection하고 C# v6.0.

private static readonly HashSet<Type> m_numTypes = new HashSet<Type>
{
    typeof(int),  typeof(double),  typeof(decimal),
    typeof(long), typeof(short),   typeof(sbyte),
    typeof(byte), typeof(ulong),   typeof(ushort),
    typeof(uint), typeof(float),   typeof(BigInteger)
};

뒤에 :

public static bool IsNumeric<T>( this T myType )
{
    var IsNumeric = false;

    if( myType != null )
    {
        IsNumeric = m_numTypes.Contains( myType.GetType() );
    }

    return IsNumeric;
}

사용법 (T item):

if ( item.IsNumeric() ) {}

null 거짓을 반환합니다.


편집 : 글쎄, 나는 더 성능이 좋도록 아래 코드를 수정 한 다음 @Hugo가 게시 한 테스트를 실행했습니다. 속도는 시퀀스의 마지막 항목 (십진수)을 사용하는 @Hugo의 IF와 거의 비슷합니다. 그러나 첫 번째 항목 '바이트'를 사용하면 케이크를 가져 가지만 성능과 관련하여 분명히 순서가 중요합니다. 아래 코드를 사용하는 것이 작성하기 쉽고 비용면에서 더 일관 적이지만 유지 관리가 가능하거나 미래를 보장 할 수는 없습니다.

Looks like switching from Type.GetTypeCode() to Convert.GetTypeCode() sped up performance drastically, about 25%, VS Enum.Parse() which was like 10 times slower.


I know this post is old but IF using the TypeCode enum method, easiest (and probably the cheapest) would be something like this:

public static bool IsNumericType(this object o)
{   
  var t = (byte)Convert.GetTypeCode(o);
  return t > 4 && t < 16;
}

Given the following enum definition for TypeCode:

public enum TypeCode
{
    Empty = 0,
    Object = 1,
    DBNull = 2,
    Boolean = 3,
    Char = 4,
    SByte = 5,
    Byte = 6,
    Int16 = 7,
    UInt16 = 8,
    Int32 = 9,
    UInt32 = 10,
    Int64 = 11,
    UInt64 = 12,
    Single = 13,
    Double = 14,
    Decimal = 15,
    DateTime = 16,
    String = 18
}

I haven't tested it thoroughly, but for basic C# numeric types, this would seem to cover it. However, as @JonSkeet mentioned, this enum isn't updated for additional types added to .NET down the road.


Switch is a little slow, bacause every time methods in the worst situation will be go through all type. I think, using Dictonary is more nice, in this situation you will be have O(1):

public static class TypeExtensions
{
    private static readonly HashSet<Type> NumberTypes = new HashSet<Type>();

    static TypeExtensions()
    {
        NumberTypes.Add(typeof(byte));
        NumberTypes.Add(typeof(decimal));
        NumberTypes.Add(typeof(double));
        NumberTypes.Add(typeof(float));
        NumberTypes.Add(typeof(int));
        NumberTypes.Add(typeof(long));
        NumberTypes.Add(typeof(sbyte));
        NumberTypes.Add(typeof(short));
        NumberTypes.Add(typeof(uint));
        NumberTypes.Add(typeof(ulong));
        NumberTypes.Add(typeof(ushort));
    }

    public static bool IsNumber(this Type type)
    {
        return NumberTypes.Contains(type);
    }
}

oops! Misread the question! Personally, would roll with Skeet's.


hrm, sounds like you want to DoSomething on Type of your data. What you could do is the following

public class MyClass
{
    private readonly Dictionary<Type, Func<SomeResult, object>> _map = 
        new Dictionary<Type, Func<SomeResult, object>> ();

    public MyClass ()
    {
        _map.Add (typeof (int), o => return SomeTypeSafeMethod ((int)(o)));
    }

    public SomeResult DoSomething<T>(T numericValue)
    {
        Type valueType = typeof (T);
        if (!_map.Contains (valueType))
        {
            throw new NotSupportedException (
                string.Format (
                "Does not support Type [{0}].", valueType.Name));
        }
        SomeResult result = _map[valueType] (numericValue);
        return result;
    }
}

참고URL : https://stackoverflow.com/questions/1749966/c-sharp-how-to-determine-whether-a-type-is-a-number

반응형