Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
.NET 7 introduce nuove interfacce generiche correlate alla matematica nella libreria di classi di base. La disponibilità di queste interfacce significa che è possibile limitare un parametro di tipo di un tipo o di un metodo generico affinché sia simile a un numero. Inoltre, C# 11 e versioni successive consentono di definire static virtual i membri dell'interfaccia. Poiché gli operatori devono essere dichiarati come static, questa nuova funzionalità C# consente di dichiarare gli operatori nelle nuove interfacce per tipi simili a numeri.
Insieme, queste innovazioni consentono di eseguire operazioni matematiche in modo generico, ovvero senza dover conoscere il tipo esatto con cui si sta lavorando. Ad esempio, se si vuole scrivere un metodo che aggiunge due numeri, in precedenza era necessario aggiungere un overload del metodo per ogni tipo , ad esempio static int Add(int first, int second) e static float Add(float first, float second). È ora possibile scrivere un singolo metodo generico, in cui il parametro di tipo è vincolato a un tipo numerico. Per esempio:
static T Add<T>(T left, T right)
where T : INumber<T>
{
return left + right;
}
In questo metodo il parametro T di tipo è vincolato a un tipo che implementa la nuova INumber<TSelf> interfaccia.
INumber<TSelf> implementa l'interfaccia IAdditionOperators<TSelf,TOther,TResult> , che contiene l'operatore + . Ciò consente al metodo di aggiungere in modo generico i due numeri. Il metodo può essere usato con uno qualsiasi dei tipi numerici predefiniti di .NET, perché sono stati tutti aggiornati per implementare INumber<TSelf> in .NET 7.
Gli autori di librerie trarranno maggior vantaggio dalle interfacce matematiche generiche, perché possono semplificare la codebase rimuovendo gli overload "ridondanti". Altri sviluppatori trarranno vantaggio indirettamente, perché le API usate possono iniziare a supportare più tipi.
Interfacce
Le interfacce sono state progettate per essere abbastanza dettagliate da permettere agli utenti di definire le proprie interfacce sopra, ma anche sufficientemente granulari da poter essere utilizzate facilmente. In tal modo, esistono alcune interfacce numeriche di base con cui la maggior parte degli utenti interagirà, ad esempio INumber<TSelf> e IBinaryInteger<TSelf>. Le interfacce con granularità più fine, ad esempio IAdditionOperators<TSelf,TOther,TResult> e ITrigonometricFunctions<TSelf>, supportano questi tipi e sono disponibili per gli sviluppatori che definiscono le proprie interfacce numeriche specifiche del dominio.
- Interfacce numeriche
- Interfacce operatore
- Interfacce di funzione
- Analisi e formattazione delle interfacce
Interfacce numeriche
In questa sezione vengono descritte le interfacce in System.Numerics che descrivono tipi simili a numeri e le funzionalità a loro disponibili.
| Nome interfaccia | Descrizione |
|---|---|
| IBinaryFloatingPointIeee754<TSelf> | Espone le API comuni ai tipi binari a virgola mobile1 che implementano lo standard IEEE 754. |
| IBinaryInteger<TSelf> | Espone le API comuni ai numeri interi binari2. |
| IBinaryNumber<TSelf> | Espone le API comuni ai numeri binari. |
| IFloatingPoint<TSelf> | Espone le API comuni ai tipi a virgola mobile. |
| IFloatingPointIeee754<TSelf> | Espone le API comuni ai tipi a virgola mobile che implementano lo standard IEEE 754. |
| INumber<TSelf> | Espone le API comuni ai tipi di numeri confrontabili (in effetti il dominio numerico "reale"). |
| INumberBase<TSelf> | Espone le API comuni a tutti i tipi di numeri (in effetti il dominio numerico "complesso"). |
| ISignedNumber<TSelf> | Espone le API comuni a tutti i tipi di numeri firmati , ad esempio il concetto di NegativeOne. |
| IUnsignedNumber<TSelf> | Espone le API comuni a tutti i tipi di numeri non firmati. |
| IAdditiveIdentity<TSelf,TResult> | Espone il concetto di (x + T.AdditiveIdentity) == x. |
| IMinMaxValue<TSelf> | Espone il concetto di T.MinValue e T.MaxValue. |
| IMultiplicativeIdentity<TSelf,TResult> | Espone il concetto di (x * T.MultiplicativeIdentity) == x. |
1I tipi binari a virgola mobile sono Double (double), Halfe Single (float).
2I tipi interi binari sono (Byte), byte (Int16), short (Int32), int (Int64), long, Int128 (IntPtr), nint (SByte), sbyte (UInt16), ushort (UInt32), uint (UInt64), ulong, e UInt128 (UIntPtr).
L'interfaccia che è più probabile usare direttamente è INumber<TSelf>, che corrisponde approssimativamente a un numero reale . Se un tipo implementa questa interfaccia, significa che un valore ha un segno (inclusi unsigned i tipi, che sono considerati positivi) e può essere confrontato con altri valori dello stesso tipo.
INumberBase<TSelf> conferisce concetti più avanzati, ad esempio numeri complessi e immaginari , ad esempio la radice quadrata di un numero negativo. Sono state create altre interfacce, ad esempio IFloatingPointIeee754<TSelf>, perché non tutte le operazioni hanno senso per tutti i tipi di numero, ad esempio il calcolo del piano di un numero ha senso solo per i tipi a virgola mobile. Nella libreria di classi di base .NET il tipo Double a virgola mobile implementa IFloatingPointIeee754<TSelf> ma Int32 non lo fa.
Diverse interfacce vengono implementate anche da vari altri tipi, tra cui Char, , DateOnlyDateTime, DateTimeOffsetDecimalGuidTimeOnlye .TimeSpan
La tabella seguente illustra alcune delle API principali esposte da ogni interfaccia.
| Interfaccia | Nome API | Descrizione |
|---|---|---|
| IBinaryInteger<TSelf> | DivRem |
Calcola il quoziente e il resto contemporaneamente. |
LeadingZeroCount |
Conta il numero di bit zero iniziali nella rappresentazione binaria. | |
PopCount |
Conta il numero di bit impostati nella rappresentazione binaria. | |
RotateLeft |
Ruota i bit a sinistra, talvolta chiamato anche rotazione circolare a sinistra. | |
RotateRight |
Ruota i bit a destra, talvolta chiamati anche spostamento circolare a destra. | |
TrailingZeroCount |
Conta il numero di bit finali zero nella rappresentazione binaria. | |
| IFloatingPoint<TSelf> | Ceiling |
Arrotonda il valore verso l'infinito positivo. +4.5 diventa +5 e -4.5 diventa -4. |
Floor |
Arrotonda il valore verso l'infinito negativo. +4.5 diventa +4 e -4.5 diventa -5. | |
Round |
Arrotonda il valore utilizzando la modalità di arrotondamento specificata. | |
Truncate |
Arrotonda il valore verso zero. +4.5 diventa +4 e -4.5 diventa -4. | |
| IFloatingPointIeee754<TSelf> | E |
Ottiene un valore che rappresenta il numero di Eulero per il tipo. |
Epsilon |
Ottiene il valore rappresentabile più piccolo maggiore di zero per il tipo. | |
NaN |
Ottiene un valore che rappresenta NaN per il tipo. |
|
NegativeInfinity |
Ottiene un valore che rappresenta -Infinity per il tipo. |
|
NegativeZero |
Ottiene un valore che rappresenta -Zero per il tipo. |
|
Pi |
Ottiene un valore che rappresenta Pi per il tipo. |
|
PositiveInfinity |
Ottiene un valore che rappresenta +Infinity per il tipo. |
|
Tau |
Ottiene un valore che rappresenta Tau (2 * Pi) per il tipo. |
|
| (Altro) | Implementa il set completo di interfacce elencate in Interfacce funzione. | |
| INumber<TSelf> | Clamp |
Limita un valore a non più e non inferiore al valore minimo e massimo specificato. |
CopySign |
Imposta il segno di un valore specificato allo stesso segno di un altro valore specificato. | |
Max |
Restituisce il valore maggiore di due valori, restituendo NaN se uno degli input è NaN. |
|
MaxNumber |
Restituisce il valore maggiore di due valori, restituendo il numero se un input è NaN. |
|
Min |
Restituisce il valore minore di due valori, restituendo NaN se uno degli input è NaN. |
|
MinNumber |
Restituisce il minore di due valori, restituendo il numero se un input è NaN. |
|
Sign |
Restituisce -1 per i valori negativi, 0 per zero e +1 per i valori positivi. | |
| INumberBase<TSelf> | One |
Ottiene il valore 1 per il tipo. |
Radix |
Ottiene la radice o la base del tipo. Int32 restituisce 2. Decimal restituisce 10. | |
Zero |
Ottiene il valore 0 per il tipo. | |
CreateChecked |
Crea un valore, generando un'eccezione OverflowException se l'input non può adattarsi.1 | |
CreateSaturating |
Crea un valore, limitato a T.MinValue o T.MaxValue se l'input non può adattarsi.1 |
|
CreateTruncating |
Crea un valore da un altro valore, circondando se l'input non può adattarsi. 1 | |
IsComplexNumber |
Restituisce vero se il valore ha una parte reale diversa da zero e una parte immaginaria diversa da zero. | |
IsEvenInteger |
Restituisce true se il valore è un numero intero pari. 2.0 restituisce truee 2.2 restituisce false. |
|
IsFinite |
Restituisce true se il valore non è infinito e non NaN. |
|
IsImaginaryNumber |
Restituisce vero se il valore ha una componente reale pari a zero. Questo significa che 0 è immaginario e 1 + 1i non lo è. |
|
IsInfinity |
Restituisce true se il valore rappresenta l'infinito. | |
IsInteger |
Restituisce true se il valore è un numero intero. 2.0 e 3.0 restituiscono truee 2.2 e 3.1 restituiscono false. |
|
IsNaN |
Restituisce true se il valore rappresenta NaN. |
|
IsNegative |
Restituisce vero se il valore è negativo. Sono inclusi -0.0. | |
IsPositive |
Restituisce true se il valore è positivo. Sono inclusi 0 e +0,0. | |
IsRealNumber |
Restituisce vero se il valore ha una parte immaginaria pari a zero. Ciò significa che 0 è reale come tutti i INumber<T> tipi. |
|
IsZero |
Restituisce true se il valore rappresenta zero. Sono inclusi 0, +0.0 e -0.0. | |
MaxMagnitude |
Restituisce il valore con un valore assoluto maggiore, restituendo NaN se uno degli input è NaN. |
|
MaxMagnitudeNumber |
Restituisce il valore con un valore assoluto maggiore, restituendo il numero se un input è NaN. |
|
MinMagnitude |
Restituisce il valore con un valore assoluto minore, restituendo NaN se uno degli input è NaN. |
|
MinMagnitudeNumber |
Restituisce il valore con un valore assoluto minore, restituendo il numero se un input è NaN. |
|
| ISignedNumber<TSelf> | NegativeOne |
Ottiene il valore -1 per il tipo. |
1Per comprendere il comportamento dei tre Create* metodi, considerare gli esempi seguenti.
Esempio quando viene specificato un valore troppo grande:
-
byte.CreateChecked(384)genererà un'eccezione OverflowException. -
byte.CreateSaturating(384)restituisce 255 perché 384 è maggiore di Byte.MaxValue (ovvero 255). -
byte.CreateTruncating(384)restituisce 128 perché prende gli 8 bit meno significativi (384 ha una rappresentazione esadecimale di0x0180, e gli 8 bit meno significativi sono0x80, cioè 128).
Esempio quando viene specificato un valore troppo piccolo:
-
byte.CreateChecked(-384)genererà un'eccezione OverflowException. -
byte.CreateSaturating(-384)restituisce 0 perché -384 è minore di Byte.MinValue (ovvero 0). -
byte.CreateTruncating(-384)restituisce 128 perché prende gli 8 bit meno significativi (384 ha una rappresentazione esadecimale di0xFE80, e gli 8 bit meno significativi sono0x80, cioè 128).
I Create* metodi hanno anche alcune considerazioni speciali per i tipi a virgola mobile IEEE 754, come float e double, poiché hanno i valori speciali PositiveInfinity, NegativeInfinity e NaN. Tutte e tre Create* le API si comportano come CreateSaturating. Inoltre, mentre MinValue e MaxValue rappresentano il numero "normale" negativo/positivo più grande, i valori minimi e massimi effettivi sono NegativeInfinity e PositiveInfinity, quindi si bloccano a questi valori.
Interfacce operatore
Le interfacce dell'operatore corrispondono ai vari operatori disponibili per il linguaggio C#.
- Non associano in modo esplicito operazioni come la moltiplicazione e la divisione perché non sono corrette per tutti i tipi. Ad esempio,
Matrix4x4 * Matrix4x4è valido, maMatrix4x4 / Matrix4x4non è valido. - In genere consentono ai tipi di input e di risultato di differire per supportare scenari quali la divisione di due interi per ottenere un
doublevalore , ad esempio ,3 / 2 = 1.5o il calcolo della media di un set di interi.
| Nome interfaccia | Operatori definiti |
|---|---|
| IAdditionOperators<TSelf,TOther,TResult> | x + y |
| IBitwiseOperators<TSelf,TOther,TResult> |
x & y, 'x | y', x ^ ye ~x |
| IComparisonOperators<TSelf,TOther,TResult> |
x < y, x > y, x <= ye x >= y |
| IDecrementOperators<TSelf> |
--x e x-- |
| IDivisionOperators<TSelf,TOther,TResult> | x / y |
| IEqualityOperators<TSelf,TOther,TResult> |
x == y e x != y |
| IIncrementOperators<TSelf> |
++x e x++ |
| IModulusOperators<TSelf,TOther,TResult> | x % y |
| IMultiplyOperators<TSelf,TOther,TResult> | x * y |
| IShiftOperators<TSelf,TOther,TResult> |
x << y e x >> y |
| ISubtractionOperators<TSelf,TOther,TResult> | x - y |
| IUnaryNegationOperators<TSelf,TResult> | -x |
| IUnaryPlusOperators<TSelf,TResult> | +x |
Annotazioni
Alcune interfacce definiscono un operatore controllato oltre a un normale operatore deselezionato. Gli operatori controllati vengono chiamati nei contesti controllati e consentono a un tipo definito dall'utente di definire il comportamento di overflow. Se si implementa un operatore verificato, ad esempio CheckedSubtraction(TSelf, TOther), è necessario implementare anche l'operatore non verificato, ad esempio Subtraction(TSelf, TOther).
Interfacce di funzione
Le interfacce di funzione definiscono API matematiche comuni che si applicano più ampiamente rispetto a un'interfaccia numerica specifica. Queste interfacce sono tutte implementate da IFloatingPointIeee754<TSelf>e possono essere implementate da altri tipi pertinenti in futuro.
| Nome interfaccia | Descrizione |
|---|---|
| IExponentialFunctions<TSelf> | Espone funzioni esponenziali che supportano e^x, e^x - 1, 2^x2^x - 1, , 10^xe 10^x - 1. |
| IHyperbolicFunctions<TSelf> | Espone funzioni iperboliche che supportano acosh(x), asinh(x), atanh(x), cosh(x), sinh(x)e tanh(x). |
| ILogarithmicFunctions<TSelf> | Espone funzioni logaritmiche che supportano ln(x), ln(x + 1), log2(x), log2(x + 1)log10(x), e log10(x + 1). |
| IPowerFunctions<TSelf> | Espone le funzioni di potenza che supportano x^y. |
| IRootFunctions<TSelf> | Espone le funzioni radice che supportano cbrt(x) e sqrt(x). |
| ITrigonometricFunctions<TSelf> | Espone le funzioni trigonometriche che supportano acos(x), asin(x), atan(x), cos(x), sin(x)e tan(x). |
Analisi e formattazione delle interfacce
L'analisi e la formattazione sono concetti fondamentali nella programmazione. Vengono comunemente usati quando si converte l'input dell'utente in un determinato tipo o viene visualizzato un tipo all'utente. Queste interfacce si trovano nello spazio dei nomi System.
| Nome interfaccia | Descrizione |
|---|---|
| IParsable<TSelf> | Espone il supporto per T.Parse(string, IFormatProvider) e T.TryParse(string, IFormatProvider, out TSelf). |
| ISpanParsable<TSelf> | Espone il supporto per T.Parse(ReadOnlySpan<char>, IFormatProvider) e T.TryParse(ReadOnlySpan<char>, IFormatProvider, out TSelf). |
| IFormattable 1 | Espone il supporto per value.ToString(string, IFormatProvider). |
| ISpanFormattable 1 | Espone il supporto per value.TryFormat(Span<char>, out int, ReadOnlySpan<char>, IFormatProvider). |
1Questa interfaccia non è nuova, né è generica. Tuttavia, viene implementato da tutti i tipi di numeri e rappresenta l'operazione inversa di IParsable.
Ad esempio, il programma seguente accetta due numeri come input, leggendoli dalla console usando un metodo generico in cui il parametro di tipo è vincolato a essere IParsable<TSelf>. Calcola la media usando un metodo generico in cui i parametri di tipo per i valori di input e risultato sono vincolati a essere INumber<TSelf>e quindi visualizzano il risultato nella console.
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
*/