Как проверить, является ли тип (общий тип) интегральным или неинтегральным типом в С#?
У меня есть общий тип T
. Использование класс Marc Operator
Я могу выполнять вычисления на нем.
Можно ли путем простых вычислений определить, является ли тип интегралом или nonintegral?
Возможно, есть лучшее решение? Я бы предпочел поддерживать любой возможный тип, поэтому я хотел бы предотвратить жесткое кодирование, типы которого являются интегральными/неинтегральными.
Фоновая информация
Ситуация, в которой я нахожусь, заключается в том, что я хочу передать double
в T
, но округлить до ближайшего значения T
до значения double
.
int a = (int)2.6
приводит к 2
, в то время как я хочу, чтобы он привел его в 3
, не зная тип (в данном случае int
). Это также может быть double
, и в этом случае я хочу, чтобы результат был 2.6
.
Ответы
Ответ 1
Вы пробовали 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));
}
Ответ 2
Здесь метод, который будет определять, является ли конкретное значение, хранящееся в общем числовом типе, целым числом без жесткого кодирования. Протестировано для меня на .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 содержит примерный код, который может быть полезен. В частности, он включает список типов, которые считаются числовыми.
Ответ 3
Я не уверен на 100%, что вы просите, но:
Чтобы проверить, является ли он целым типом, используйте это: if (obj is float || obj is double)
или if typeof(T) == typeof(float) || typeof(T) == typeof(double))
Чтобы проверить, является ли это интегральным значением, переведите его в double и затем if(value == Math.Round(value))
Конечно, предполагается, что у вас есть номер в первую очередь. Я считаю, что класс Operator, который вы используете, поддерживает такие вещи, как DateTime. Было бы лучше, если бы ваш общий метод имел общее ограничение where T : IConvertible
? Таким образом, были бы явные методы ToDouble
и ToInteger
.
Edit
Думаю, я понимаю: у вас есть две локальные переменные, double d; T num;
. Вы хотите использовать d
для ввода T
, но с правильным округлением, если T
является интегральным типом. Это правильно?
Предполагая, что правильно, вот что я сделал бы:
public void SomeMethod<T>()
{
double d;
// I think I got all the floating-point types. There 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));
}
Ответ 4
Ответ Криса дает возможное решение упомянутого сценария, но по соображениям производительности я все еще пытаюсь ответить на фактический вопрос.
Предполагаемое (непроверенное) значение Convert.ChangeType
намного медленнее, чем Math.Round()
. В идеале я могу проверить один раз, является ли данный тип интегральным или нет, и условно вызывать Math.Round()
с этого момента, чтобы получить гораздо более эффективное решение, чем постоянное вызов Convert.ChangeType()
.
Я пытаюсь выполнить следующую реализацию:
- Преобразуйте как
3
, 2
, так и 1
в нужный неизвестный тип. (Это предполагает, что возможно преобразование из int
в числовой тип, что всегда должно быть возможно.)
- В случае
3 / 2 == 1
он является интегральным типом. В противном случае это нецелый тип.
Это решение нигде не опирается на знание типа и использует только конверсии и вычисления.