Проверьте, равно ли значение 0 с помощью метода расширения
У меня есть метод расширения, который выглядит как
public static T ThrowIfObjectIsNull<T>(this T argument) where T : class
{
if (argument == null)
throw new ArgumentNullException(nameof(argument));
return argument;
}
Это в основном проверяет, не передается ли объект, который передается. То, что я пытаюсь сделать, это создать другой метод расширения, в котором значение int
которое передается, не равно 0. Поэтому я пошел и создал:
public static T ThrowIfZero<T>(this T argument) where T : struct
{
if (argument == 0)
throw new ArgumentOutOfRangeException("some error here");
return argument;
}
Конечно, вышесказанное не компилирует, предлагая ошибку:
Ошибка CS0019 Оператор '==' не может применяться к операндам типа 'T' и 'int'
Может ли кто-нибудь указать мне в правильном направлении, как я могу проверить, не значение аргумента не 0
?
Ответы
Ответ 1
Вы можете просто использовать Equals
:
public static T ThrowIfZero<T>(this T argument) where T : struct
{
if (argument.Equals(0))
throw new ArgumentOutOfRangeException("some error here");
return argument;
}
Но это не очень хорошо работает, если аргументом является, например, десятичный 0.0m
который не равен целому числу 0
поскольку Питер правильно прокомментировал.
Поэтому, если вам нужна версия, которая работает для любого номера, вы можете использовать этот подход:
public static T ThrowIfZero<T>(this T argument) where T : struct
{
bool isZero = Decimal.Compare(0.0m, Convert.ToDecimal(argument)) == 0;
if (isZero)
throw new ArgumentOutOfRangeException("some error here");
return argument;
}
Ответ 2
Вы также можете использовать EqualityComparer.
public static T ThrowIfZero<T>(this T argument) where T : struct
{
if (EqualityComparer<T>.Default.Equals(argument, default(T)))
throw new ArgumentOutOfRangeException("some error here");
return argument;
}
Вы можете направить ответ на этот пост (кредит должен идти на @Mehrdad).
Ответ 3
Это не похоже, что вам нужны дженерики вообще. Если переменная является просто int, как вы предлагаете, просто используйте:
public static int ThrowIfZero(this int argument)
{
if (argument == 0)
{
throw new ArgumentOutOfRangeException("some error here");
}
return argument;
}
Ответ 4
int
, decimal
и т.д. реализуют IComparable
поэтому что-то вроде этого также работает:
public static T ThrowIfZero<T>(this T argument)
where T : struct, IComparable
{
if (argument.CompareTo(default(T)) == 0)
throw new ArgumentOutOfRangeException("some error here");
return argument;
}
Ответ 5
Еще один способ, который я видел недавно:
public static int ThrowIfZero<T>(this T param)
where T : struct
{
var o = (object)param;
int i;
try { i = (int)o; }
catch { throw new ArgumentException("Param must be of type int"); }
if (i == 0) throw new ArgumentException("Must be not be zero");
return (int)(object)param;
}
Мы можем обмануть компилятор, позволив нам направить T
в int
, сначала набросив его на object
. Это отлично работает для int
, но недостатком является то, что это работает только для int
и не будет работать для float
.
Если вы хотите, чтобы он работал со всеми численными типами, вы можете использовать сопоставление с образцом и сделать что-то вроде этого:
public static T ThrowIfZero<T>(this T param)
where T : struct
{
switch (param)
{
case int i: if (i == 0) throwException(); break;
case double d: if (d == 0) throwException(); break;
case float f: if (f == 0) throwException(); break;
case decimal c: if (c == 0) throwException(); break;
case long l: if (l == 0) throwException(); break;
case uint ui: if (ui == 0) throwException(); break;
case ulong ul: if (ul == 0) throwException(); break;
case byte b: if (b == 0) throwException(); break;
default: throw new ArgumentException("Invalid Type");
}
return param;
// ---- Local Functions ---- //
void throwException() => throw new ArgumentException("Must not be zero");
}
Конечно, лучшим решением было бы, если бы они взяли Джона Скита в свое предложение и сделали там, where T: numeric
что ограничивает его в основном вышеприведенными типами и, возможно, некоторыми настраиваемыми типами, поэтому мы можем включить наш собственный ComplexNumber
или SuperBigInteger
.
Честно говоря, я бы не делал этого ни одним из этих способов, потому что первый способ кастинга, а второй способ просто приводит к отсутствию дел и необходимости поддерживать switch
если вам нужно, просто подумал, что я покажу больше вариантов, хоть.