Чи є спосіб визначити, чи вказаний тип .Net - це число? Наприклад: System.UInt32/UInt16/Doubleчи всі числа. Я хочу уникати тривалого вимикача на корпусі Type.FullName.
Чи є спосіб визначити, чи вказаний тип .Net - це число? Наприклад: System.UInt32/UInt16/Doubleчи всі числа. Я хочу уникати тривалого вимикача на корпусі Type.FullName.
Відповіді:
Спробуйте це:
Type type = object.GetType();
bool isNumber = (type.IsPrimitiveImple && type != typeof(bool) && type != typeof(char));
Примітивними типами є булевий, байт, 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
decimalтип не є числовим?
decimal це число. Тільки тому, що це не примітив, не означає, що він не числовий. Ваш код повинен враховувати це.
Не використовуйте перемикач - просто використовуйте набір:
HashSet<Type> NumericTypes = new HashSet<Type>
{
typeof(decimal), typeof(byte), typeof(sbyte),
typeof(short), typeof(ushort), ...
};
EDIT: Однією з переваг цього над використанням типу типу є те, що при введенні нових числових типів у .NET (наприклад, BigInteger та Complex ) легко налаштувати - тоді як ці типи не отримають код типу.
switchпросто не працює Type, тому не можна. Можна, TypeCodeзвичайно, увімкнути , але це вже інша справа.
Жодне з рішень не враховує Nullable.
Я трохи змінив рішення Джона Скіта:
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. Але це рішення дозволяє уникнути небезпеки забути додати певний 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;
}
}
Зверніть увагу на видалення оптимізації (див. Коментарі до enzi).
Якщо ви дійсно хочете її оптимізувати (втрачаючи читабельність та певну безпеку ...):
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;
}
return unchecked((uint)Type.GetTypeCode(type) - 5u) <= 10u;видалити гілку, яку ввів &&.
В основному рішення 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 для 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чисельний.
Завдяки 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);
}
Ви можете використовувати Type.IsPrimitive, а потім розібратися Booleanта Charтипи, приблизно так:
bool IsNumeric(Type type)
{
return type.IsPrimitive && type!=typeof(char) && type!=typeof(bool);
}
EDIT : Ви можете також виключити типи IntPtrта UIntPtrтипи, якщо ви не вважаєте їх числовими.
decimalтип не є числовим?
Розширення типу з підтримкою нульового типу.
public static bool IsNumeric(this Type type)
{
if (type == null) { return false; }
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
{
type = type.GetGenericArguments()[0];
}
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;
}
}
Модифікований Скит і рішення 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 повертає помилкове.
Перемикання відбувається трохи повільно, тому що кожного разу методи в найгіршій ситуації будуть проходити через усі типи. Я думаю, що використовувати Dictonary приємніше, у цій ситуації вам доведеться 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);
}
}
Спробуйте TypeSupport NuGet пакет для C #. Він має підтримку для виявлення всіх числових типів (серед багатьох інших функцій):
var extendedType = typeof(int).GetExtendedType();
Assert.IsTrue(extendedType.IsNumericType);
На жаль, ці типи не мають багато спільного, крім всіх типів цінності. Але, щоб уникнути тривалого переключення випадків, ви можете просто визначити список, який читається лише з усіма цими типами, а потім просто перевірити, чи вказаний тип знаходиться у списку.
Всі вони мають цінні типи (крім bool та, можливо, перерахунків). Таким чином, ви можете просто використовувати:
bool IsNumberic(object o)
{
return (o is System.ValueType && !(o is System.Boolean) && !(o is System.Enum))
}
struct... Я не думаю, що саме цього ви хочете.
EDIT: Ну, я змінив код нижче, щоб бути більш ефективним, а потім запустив тести, опубліковані @Hugo проти нього. Швидкість приблизно нарівнюється із IF IF @ Hugo, використовуючи останній елемент у його послідовності (Десяткові). Якщо, однак, використовуючи перший пункт "байт", він бере торт, але чітко впорядковує питання, коли справа доходить до виконання. Хоча використання наведеного нижче коду простіше писати та більш послідовно щодо його вартості, однак це не є ремонтом чи можливістю в майбутньому.
Схоже, перехід з Type.GetTypeCode () на Convert.GetTypeCode () значно збільшив продуктивність, приблизно на 25%, VS Enum.Parse (), що було в 10 разів повільніше.
Я знаю, що ця публікація стара, але якщо за допомогою методу перерахування TypeCode найпростіший (і, мабуть, найдешевший) був би щось подібне:
public static bool IsNumericType(this object o)
{
var t = (byte)Convert.GetTypeCode(o);
return t > 4 && t < 16;
}
З огляду на таке визначення enum для 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
}
Я не перевірив її ретельно, але для основних числових типів C # це, мабуть, охоплює це. Однак, як згадував @JonSkeet, цей перелік не оновлюється для додаткових типів, доданих до .NET вниз.
ой! Непрочитайте питання! Особисто, котиться зі Скетом .
HRM, звучить , як ви хочете , щоб DoSomethingна Typeваших даних. Що ви можете зробити, це наступне
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;
}
}