Арифметические операторы в универсальных типах
.NET 7 представляет новые универсальные интерфейсы, связанные с математикой, в библиотеке базовых классов. Доступность этих интерфейсов означает, что параметр типа универсального типа или метода может быть "числовым". Кроме того, C# 11 и более поздних версий позволяет определять static virtual
элементы интерфейса. Так как операторы должны быть объявлены как static
, эта новая функция C# позволяет объявлять операторы в новых интерфейсах для типов, таких как число.
Вместе эти инновации позволяют выполнять математические операции универсально, т. е. не зная точного типа, с которым вы работаете. Например, если вы хотите написать метод, добавляющий два числа, ранее пришлось добавить перегрузку метода для каждого типа (например, static int Add(int first, int second)
и static float Add(float first, float second)
). Теперь можно написать один универсальный метод, где параметр типа ограничен числом типа. Например:
static T Add<T>(T left, T right)
where T : INumber<T>
{
return left + right;
}
В этом методе параметр T
типа ограничен типом, реализующим новый INumber<TSelf> интерфейс. INumber<TSelf>IAdditionOperators<TSelf,TOther,TResult> реализует интерфейс, содержащий оператор +. Это позволяет методу универсально добавлять два числа. Этот метод можно использовать с любым из. Встроенные числовые типы NET, так как они были обновлены для реализации INumber<TSelf> в .NET 7.
Авторы библиотеки получают большую выгоду от универсальных математических интерфейсов, так как они могут упростить базу кода, удаляя избыточные перегрузки. Другие разработчики будут использовать косвенно, так как api, которые они используют, могут начать поддерживать больше типов.
Интерфейсы
Интерфейсы были разработаны достаточно точно, чтобы пользователи могли определять собственные интерфейсы поверх, а также быть достаточно детализированными, чтобы они легко потребляли. В этой степени существует несколько основных числовых интерфейсов, с которыми большинство пользователей будут взаимодействовать, например INumber<TSelf> и IBinaryInteger<TSelf>. Более подробные интерфейсы, такие как IAdditionOperators<TSelf,TOther,TResult> и ITrigonometricFunctions<TSelf>, поддерживают эти типы и доступны разработчикам, которые определяют собственные числовые интерфейсы, относящиеся к домену.
Числовые интерфейсы
В этом разделе описываются интерфейсы, описывающие System.Numerics типы числа и доступные для них функциональные возможности.
Имя интерфейса | Description |
---|---|
IBinaryFloatingPointIeee754<TSelf> | Предоставляет ИНТЕРФЕЙСы API, общие для типовс плавающей запятой 1, реализующих стандарт IEEE 754. |
IBinaryInteger<TSelf> | Предоставляет ИНТЕРФЕЙСы API, общие для двоичных целыхчисел 2. |
IBinaryNumber<TSelf> | Предоставляет API-интерфейсы, общие для двоичных чисел. |
IFloatingPoint<TSelf> | Предоставляет ИНТЕРФЕЙСы API, общие для типов с плавающей запятой. |
IFloatingPointIeee754<TSelf> | Предоставляет ИНТЕРФЕЙСы API, общие для типов с плавающей запятой, реализующих стандарт IEEE 754. |
INumber<TSelf> | Предоставляет ИНТЕРФЕЙСы API, общие для сопоставимых типов чисел (фактически "реальный" домен чисел). |
INumberBase<TSelf> | Предоставляет ИНТЕРФЕЙСы API, общие для всех типов чисел (фактически "сложный" домен номеров). |
ISignedNumber<TSelf> | Предоставляет ИНТЕРФЕЙСы API, общие для всех подписанных типов чисел (например, концепции NegativeOne ). |
IUnsignedNumber<TSelf> | Предоставляет ИНТЕРФЕЙСы API, общие для всех типов незначенных чисел. |
IAdditiveIdentity<TSelf,TResult> | Предоставляет концепцию (x + T.AdditiveIdentity) == x . |
IMinMaxValue<TSelf> | Предоставляет концепцию T.MinValue и T.MaxValue . |
IMultiplicativeIdentity<TSelf,TResult> | Предоставляет концепцию (x * T.MultiplicativeIdentity) == x . |
1 Двоичные типы с плавающей запятой (double
Double), Halfи Single (float
).
2. Типы целых чиселBytebyte
(), (), Int16 (short
), Int32 (int
), Int64IntPtrnint
SByteInt128UInt16ushort
sbyte
uint
long
UInt32 (ulong
) UInt128UInt64 и UIntPtr ().nuint
Интерфейс, который вы, скорее всего, используется напрямуюINumber<TSelf>, который примерно соответствует реальному числу. Если тип реализует этот интерфейс, это означает, что значение имеет знак ( unsigned
включает типы, которые считаются положительными) и можно сравнить с другими значениями того же типа. INumberBase<TSelf> предоставляет более сложные понятия, такие как сложные и мнимые числа, например квадратный корень отрицательного числа. Другие интерфейсы, например, были созданы, так как IFloatingPointIeee754<TSelf>не все операции имеет смысл для всех типов чисел, например вычисление пола числа имеет смысл только для типов с плавающей запятой. В библиотеке базовых классов .NET тип Double с плавающей запятой реализует IFloatingPointIeee754<TSelf> , но Int32 не выполняет.
Некоторые интерфейсы также реализуются различными другими типами, включая Char, , DateOnly, DateTime, DateTimeOffset, GuidDecimalTimeOnlyи .TimeSpan
В следующей таблице показаны некоторые основные API, предоставляемые каждым интерфейсом.
Интерфейс | Имя API | Description |
---|---|---|
IBinaryInteger<TSelf> | DivRem |
Вычисляет цитент и оставшуюся часть одновременно. |
LeadingZeroCount |
Подсчитывает число начальных нулевых битов в двоичном представлении. | |
PopCount |
Подсчитывает количество битов набора в двоичном представлении. | |
RotateLeft |
Поворот битов влево, иногда также называется циклическим сдвигом влево. | |
RotateRight |
Поворот битов вправо, иногда также называется циклическим сдвигом вправо. | |
TrailingZeroCount |
Подсчитывает число конечных нулей в двоичном представлении. | |
IFloatingPoint<TSelf> | Ceiling |
Округляет значение в сторону положительной бесконечности. +4,5 становится +5, а -4,5 становится -4. |
Floor |
Округляет значение в сторону отрицательной бесконечности. +4,5 становится +4, а -4,5 становится -5. | |
Round |
Округляет значение с помощью указанного режима округления. | |
Truncate |
Округляет значение к нулю. +4,5 становится +4, а -4,5 становится -4. | |
IFloatingPointIeee754<TSelf> | E |
Возвращает значение, представляющее число Эйлера для типа. |
Epsilon |
Возвращает наименьшее представляющее значение, которое больше нуля для типа. | |
NaN |
Возвращает значение, представляющее NaN тип. |
|
NegativeInfinity |
Возвращает значение, представляющее -Infinity тип. |
|
NegativeZero |
Возвращает значение, представляющее -Zero тип. |
|
Pi |
Возвращает значение, представляющее Pi тип. |
|
PositiveInfinity |
Возвращает значение, представляющее +Infinity тип. |
|
Tau |
Возвращает значение, представляющее Tau (2 * Pi ) для типа. |
|
(Другое) | (Реализует полный набор интерфейсов, перечисленных в разделе Интерфейсы функций.) | |
INumber<TSelf> | Clamp |
Ограничивает значение не более и не меньше указанного минимального и максимального значения. |
CopySign |
Задает знак указанного значения таким же, как и другое указанное значение. | |
Max |
Возвращает больше двух значений, возвращая NaN при наличии входных данных NaN . |
|
MaxNumber |
Возвращает больше двух значений, возвращая число, если один входной NaN . |
|
Min |
Возвращает меньшее из двух значений, возвращая NaN при наличии входных данных NaN . |
|
MinNumber |
Возвращает меньшее из двух значений, возвращая число, если одно входное значение NaN . |
|
Sign |
Возвращает значение -1 для отрицательных значений, от 0 до нуля и +1 для положительных значений. | |
INumberBase<TSelf> | One |
Возвращает значение 1 для типа. |
Radix |
Возвращает радикс или базу для типа. Int32 возвращает 2. Decimal возвращает 10. | |
Zero |
Возвращает значение 0 для типа. | |
CreateChecked |
Создает значение, вызывая OverflowException исключение, если входные данные не могут соответствовать.1 | |
CreateSaturating |
Создает значение, зажимая или T.MinValue T.MaxValue если входные данные не могут соответствовать.1 |
|
CreateTruncating |
Создает значение из другого значения, обтекая вокруг, если входные данные не могут соответствовать.1 | |
IsComplexNumber |
Возвращает значение true, если значение имеет ненульную реальную часть и ненульную мнимую часть. | |
IsEvenInteger |
Возвращает значение true, если значение является даже целым числом. 2.0 возвращает true и возвращает false 2.2. |
|
IsFinite |
Возвращает значение true, если значение не бесконечно, а не NaN . |
|
IsImaginaryNumber |
Возвращает значение true, если значение имеет нулю реальную часть. Это означает 0 , что это мнимый и 1 + 1i не является. |
|
IsInfinity |
Возвращает значение true, если значение представляет бесконечность. | |
IsInteger |
Возвращает значение true, если значение является целым числом. Возврат 2.0 и 3.0 true , а также возврат 2.2 и 3.1 false . |
|
IsNaN |
Возвращает значение true, если значение представляет NaN . |
|
IsNegative |
Возвращает значение true, если значение отрицательное. Это включает в себя -0.0. | |
IsPositive |
Возвращает значение true, если значение является положительным. Это включает 0 и +0,0. | |
IsRealNumber |
Возвращает значение true, если значение имеет нулевая мнимая часть. Это означает, что 0 является реальным, как и все INumber<T> типы. |
|
IsZero |
Возвращает значение true, если значение представляет ноль. Это включает 0, +0.0 и -0.0. | |
MaxMagnitude |
Возвращает значение с большим абсолютным значением, возвращая NaN , если входные данные имеют значение NaN . |
|
MaxMagnitudeNumber |
Возвращает значение с большим абсолютным значением, возвращая число, если один входной.NaN |
|
MinMagnitude |
Возвращает значение с меньшим абсолютным значением, возвращая NaN , если входные данные имеют значение NaN . |
|
MinMagnitudeNumber |
Возвращает значение с меньшим абсолютным значением, возвращая число, если одно входное значение NaN . |
|
ISignedNumber<TSelf> | NegativeOne |
Возвращает значение -1 для типа. |
1. Чтобы понять поведение трех Create*
методов, рассмотрим следующие примеры.
Пример, если задано слишком большое значение:
byte.CreateChecked(384)
вызовет OverflowExceptionисключение .byte.CreateSaturating(384)
возвращает значение 255, так как 384 больше Byte.MaxValue (что составляет 255).byte.CreateTruncating(384)
возвращает 128, так как он принимает наименьшее 8 бит (384 имеет шестнадцатеричное представление0x0180
, а наименьшее 8 битов —0x80
128).
Пример, если задано слишком небольшое значение:
byte.CreateChecked(-384)
вызовет OverflowExceptionисключение .byte.CreateSaturating(-384)
возвращает значение 0, так как -384 меньше Byte.MinValue (что равно 0).byte.CreateTruncating(-384)
возвращает 128, так как он принимает наименьшее 8 бит (384 имеет шестнадцатеричное представление0xFE80
, а наименьшее 8 битов —0x80
128).
Методы Create*
также имеют некоторые особые аспекты для типов с плавающей запятой IEEE 754, например float
и , так как они имеют специальные значения PositiveInfinity
, NegativeInfinity
и double
NaN
. Все три Create*
API ведут себя как CreateSaturating
. Кроме того, в то время как MinValue
и MaxValue
представляет наибольшее отрицательное или положительное "нормальное" число, фактические и максимальные значения, NegativeInfinity
PositiveInfinity
поэтому они зажимаются с этими значениями.
Интерфейсы операторов
Интерфейсы операторов соответствуют различным операторам, доступным для языка C#.
- Они явно не объединяют такие операции, как умножение и деление, так как это не правильно для всех типов. Например, допустимо,
Matrix4x4 * Matrix4x4
ноMatrix4x4 / Matrix4x4
не является допустимым. - Обычно они позволяют типам входных и результирующих данных отличаться для сценариев поддержки, таких как деление двух целых чисел для получения
double
, например,3 / 2 = 1.5
или вычисления среднего значения набора целых чисел.
Имя интерфейса | Определенные операторы |
---|---|
IAdditionOperators<TSelf,TOther,TResult> | x + y |
IBitwiseOperators<TSelf,TOther,TResult> | x & y , 'x | y', x ^ y и ~x |
IComparisonOperators<TSelf,TOther,TResult> | x < y , x > y , x <= y и x >= y |
IDecrementOperators<TSelf> | --x и x-- |
IDivisionOperators<TSelf,TOther,TResult> | x / y |
IEqualityOperators<TSelf,TOther,TResult> | x == y и x != y |
IIncrementOperators<TSelf> | ++x и x++ |
IModulusOperators<TSelf,TOther,TResult> | x % y |
IMultiplyOperators<TSelf,TOther,TResult> | x * y |
IShiftOperators<TSelf,TOther,TResult> | x << y и x >> y |
ISubtractionOperators<TSelf,TOther,TResult> | x - y |
IUnaryNegationOperators<TSelf,TResult> | -x |
IUnaryPlusOperators<TSelf,TResult> | +x |
Примечание.
Некоторые интерфейсы определяют оператор проверка в дополнение к обычному оператору un проверка ed. Проверенные операторы вызываются в проверка контекстах и позволяют определяемого пользователем типа определять поведение переполнения. Если вы реализуете оператор проверка, например, CheckedSubtraction(TSelf, TOther)необходимо также реализовать оператор un проверка ed, напримерSubtraction(TSelf, TOther).
Интерфейсы функций
Интерфейсы функций определяют общие математические API, которые применяются более широко, чем к определенному числового интерфейса. Эти интерфейсы реализуются всеми и могут быть реализованы IFloatingPointIeee754<TSelf>другими соответствующими типами в будущем.
Имя интерфейса | Description |
---|---|
IExponentialFunctions<TSelf> | Предоставляет экспоненциальные функции, поддерживающие e^x , e^x - 1 , , 2^x и2^x - 1 10^x .10^x - 1 |
IHyperbolicFunctions<TSelf> | Предоставляет гиперболические функции, поддерживающие acosh(x) , , , atanh(x) иcosh(x) sinh(x) .tanh(x) asinh(x) |
ILogarithmicFunctions<TSelf> | Предоставляет логарифмические функции, поддерживающие ln(x) , ln(x + 1) , , log2(x) иlog2(x + 1) log10(x) .log10(x + 1) |
IPowerFunctions<TSelf> | Предоставляет вспомогательные x^y функции питания. |
IRootFunctions<TSelf> | Предоставляет вспомогательные cbrt(x) sqrt(x) и вспомогательные функции корневых функций. |
ITrigonometricFunctions<TSelf> | Предоставляет функции тригонометрики, поддерживающие acos(x) , asin(x) , , atan(x) , cos(x) sin(x) и tan(x) . |
Анализ и форматирование интерфейсов
Анализ и форматирование являются основными понятиями программирования. Они часто используются при преобразовании входных данных пользователя в заданный тип или отображении типа для пользователя. Эти интерфейсы находятся в System пространстве имен.
Имя интерфейса | Description |
---|---|
IParsable<TSelf> | Предоставляет поддержку T.Parse(string, IFormatProvider) и T.TryParse(string, IFormatProvider, out TSelf) . |
ISpanParsable<TSelf> | Предоставляет поддержку T.Parse(ReadOnlySpan<char>, IFormatProvider) и T.TryParse(ReadOnlySpan<char>, IFormatProvider, out TSelf) . |
IFormattable1 | Предоставляет поддержку value.ToString(string, IFormatProvider) . |
ISpanFormattable1 | Предоставляет поддержку value.TryFormat(Span<char>, out int, ReadOnlySpan<char>, IFormatProvider) . |
1Этот интерфейс не является новым, и не является универсальным. Однако он реализуется всеми типами чисел и представляет обратную операцию IParsable
.
Например, следующая программа принимает два числа в качестве входных данных, считывая их из консоли с помощью универсального метода, в котором параметр типа ограничен.IParsable<TSelf> Он вычисляет среднее значение с помощью универсального метода, в котором параметры типа для входных и результирующих значений ограничены INumber<TSelf>, а затем отображаются результаты в консоли.
using System.Globalization;
using System.Numerics;
static TResult Average<T, TResult>(T first, T second)
where T : INumber<T>
where TResult : INumber<TResult>
{
return TResult.CreateChecked( (first + second) / T.CreateChecked(2) );
}
static T ParseInvariant<T>(string s)
where T : IParsable<T>
{
return T.Parse(s, CultureInfo.InvariantCulture);
}
Console.Write("First number: ");
var left = ParseInvariant<float>(Console.ReadLine());
Console.Write("Second number: ");
var right = ParseInvariant<float>(Console.ReadLine());
Console.WriteLine($"Result: {Average<float, float>(left, right)}");
/* This code displays output similar to:
First number: 5.0
Second number: 6
Result: 5.5
*/