Allmän matematik
.NET 7 introducerar nya matematiska allmänna gränssnitt i basklassbiblioteket. Tillgängligheten för dessa gränssnitt innebär att du kan begränsa en typparameter av en allmän typ eller metod till "sifferliknande". Dessutom kan du med C# 11 och senare definiera static virtual
gränssnittsmedlemmar. Eftersom operatorer måste deklareras som static
låter den här nya C#-funktionen operatorer deklareras i de nya gränssnitten för nummerliknande typer.
Tillsammans gör de här innovationerna att du kan utföra matematiska operationer generiskt, det vill: utan att behöva veta exakt vilken typ du arbetar med. Om du till exempel vill skriva en metod som lägger till två tal, var du tidigare tvungen att lägga till en överlagring av metoden för varje typ (till exempel static int Add(int first, int second)
och static float Add(float first, float second)
). Nu kan du skriva en enda allmän metod, där typparametern är begränsad till en talliknande typ. Till exempel:
static T Add<T>(T left, T right)
where T : INumber<T>
{
return left + right;
}
I den här metoden är typparametern T
begränsad till en typ som implementerar det nya INumber<TSelf> gränssnittet. INumber<TSelf>implementerar IAdditionOperators<TSelf,TOther,TResult> gränssnittet, som innehåller operatorn + . Det gör att metoden allmänt kan lägga till de två talen. Metoden kan användas med någon av . NET:s inbyggda numeriska typer eftersom de alla har uppdaterats för implementering INumber<TSelf> i .NET 7.
Biblioteksförfattare drar mest nytta av de allmänna matematiska gränssnitten eftersom de kan förenkla sin kodbas genom att ta bort "redundanta" överlagringar. Andra utvecklare kommer att gynnas indirekt, eftersom DE API:er som de använder kan börja stödja fler typer.
Gränssnitten
Gränssnitten har utformats för att vara tillräckligt detaljerade så att användarna kan definiera sina egna gränssnitt ovanpå, samtidigt som de är tillräckligt detaljerade för att de ska vara lätta att använda. I den utsträckningen finns det några centrala numeriska gränssnitt som de flesta användare interagerar med, till exempel INumber<TSelf> och IBinaryInteger<TSelf>. De mer detaljerade gränssnitten, till exempel IAdditionOperators<TSelf,TOther,TResult> och ITrigonometricFunctions<TSelf>, stöder dessa typer och är tillgängliga för utvecklare som definierar sina egna domänspecifika numeriska gränssnitt.
Numeriska gränssnitt
I det här avsnittet beskrivs gränssnitten i System.Numerics som beskriver sifferliknande typer och vilka funktioner som är tillgängliga för dem.
Gränssnittsnamn | beskrivning |
---|---|
IBinaryFloatingPointIeee754<TSelf> | Exponerar API:er som är gemensamma för binära flyttalstyper1 som implementerar IEEE 754-standarden. |
IBinaryInteger<TSelf> | Exponerar API:er som är gemensamma för binära heltal2. |
IBinaryNumber<TSelf> | Exponerar API:er som är gemensamma för binära tal. |
IFloatingPoint<TSelf> | Exponerar API:er som är gemensamma för flyttalstyper. |
IFloatingPointIeee754<TSelf> | Exponerar API:er som är gemensamma för flyttalstyper som implementerar IEEE 754-standarden. |
INumber<TSelf> | Exponerar API:er som är gemensamma för jämförbara taltyper (i själva verket den "riktiga" taldomänen). |
INumberBase<TSelf> | Exponerar API:er som är gemensamma för alla nummertyper (i praktiken den "komplexa" taldomänen). |
ISignedNumber<TSelf> | Exponerar API:er som är gemensamma för alla signerade nummertyper (till exempel begreppet NegativeOne ). |
IUnsignedNumber<TSelf> | Exponerar API:er som är gemensamma för alla osignerade nummertyper. |
IAdditiveIdentity<TSelf,TResult> | Exponerar begreppet (x + T.AdditiveIdentity) == x . |
IMinMaxValue<TSelf> | Exponerar begreppet T.MinValue och T.MaxValue . |
IMultiplicativeIdentity<TSelf,TResult> | Exponerar begreppet (x * T.MultiplicativeIdentity) == x . |
1De binära flyttalstyperna är Double (double
), Half, och Single (float
).
2De binära heltalstyperna är Byte (byte
), Int16 (short
), (), Int32 (int
), Int64 (long
), Int128, IntPtr (nint
), SByte (sbyte
), UInt16 (), UInt32 (ushort
uint
), UInt64 (ulong
), UInt128och UIntPtr (nuint
).
Det gränssnitt som du troligen använder direkt är INumber<TSelf>, vilket ungefär motsvarar ett verkligt tal. Om en typ implementerar det här gränssnittet innebär det att ett värde har ett tecken (detta inkluderar unsigned
typer som anses vara positiva) och kan jämföras med andra värden av samma typ. INumberBase<TSelf> ger mer avancerade begrepp, till exempel komplexa och imaginära tal, till exempel kvadratroten för ett negativt tal. Andra gränssnitt, till exempel IFloatingPointIeee754<TSelf>, skapades eftersom inte alla åtgärder är meningsfulla för alla taltyper, till exempel är det bara meningsfullt att beräkna golvet i ett tal för flyttalstyper. I .NET-basklassbiblioteket implementerar IFloatingPointIeee754<TSelf> flyttalstypen Double men Int32 gör det inte.
Flera av gränssnitten implementeras också av olika typer, inklusive Char, DateOnly, DateTime, DateTimeOffset, Decimal, Guid, TimeOnlyoch TimeSpan.
I följande tabell visas några av de kärn-API:er som exponeras av varje gränssnitt.
Gränssnitt | API-namn | beskrivning |
---|---|---|
IBinaryInteger<TSelf> | DivRem |
Beräknar kvoten och resten samtidigt. |
LeadingZeroCount |
Räknar antalet inledande noll bitar i den binära representationen. | |
PopCount |
Räknar antalet angivna bitar i den binära representationen. | |
RotateLeft |
Roterar bitar åt vänster, kallas ibland även för ett cirkulärt vänsterskift. | |
RotateRight |
Roterar bitar åt höger, ibland även kallat cirkelrätt skift. | |
TrailingZeroCount |
Räknar antalet avslutande noll bitar i den binära representationen. | |
IFloatingPoint<TSelf> | Ceiling |
Avrundar värdet mot positiv oändlighet. +4,5 blir +5 och -4,5 blir -4. |
Floor |
Avrundar värdet mot negativ oändlighet. +4,5 blir +4 och -4,5 blir -5. | |
Round |
Avrundar värdet med det angivna avrundningsläget. | |
Truncate |
Avrundar värdet mot noll. +4,5 blir +4 och -4,5 blir -4. | |
IFloatingPointIeee754<TSelf> | E |
Hämtar ett värde som representerar Eulers tal för typen. |
Epsilon |
Hämtar det minsta representerande värdet som är större än noll för typen. | |
NaN |
Hämtar ett värde som representerar NaN för typen. |
|
NegativeInfinity |
Hämtar ett värde som representerar -Infinity för typen. |
|
NegativeZero |
Hämtar ett värde som representerar -Zero för typen. |
|
Pi |
Hämtar ett värde som representerar Pi för typen. |
|
PositiveInfinity |
Hämtar ett värde som representerar +Infinity för typen. |
|
Tau |
Hämtar ett värde som representerar Tau (2 * Pi ) för typen. |
|
(Övrigt) | (Implementerar den fullständiga uppsättningen gränssnitt som anges under Funktionsgränssnitt.) | |
INumber<TSelf> | Clamp |
Begränsar ett värde till högst och inte mindre än det angivna minsta och högsta värdet. |
CopySign |
Anger tecknet för ett angivet värde till samma som ett annat angivet värde. | |
Max |
Returnerar större av två värden och returnerar NaN om någon av indata är NaN . |
|
MaxNumber |
Returnerar det större av två värden och returnerar talet om en indata är NaN . |
|
Min |
Returnerar mindre av två värden och returnerar NaN om någon av indata är NaN . |
|
MinNumber |
Returnerar mindre av två värden och returnerar talet om en indata är NaN . |
|
Sign |
Returnerar -1 för negativa värden, 0 för noll och +1 för positiva värden. | |
INumberBase<TSelf> | One |
Hämtar värdet 1 för typen. |
Radix |
Hämtar radixet, eller basen, för typen. Int32 returnerar 2. Decimal returnerar 10. | |
Zero |
Hämtar värdet 0 för typen. | |
CreateChecked |
Skapar ett värde och genererar ett OverflowException om indata inte får plats.1 | |
CreateSaturating |
Skapar ett värde, klämmer fast T.MinValue eller T.MaxValue om indata inte får plats.1 |
|
CreateTruncating |
Skapar ett värde från ett annat värde och omsluter om indata inte får plats.1 | |
IsComplexNumber |
Returnerar true om värdet har en icke-noll verklig del och en icke-noll imaginär del. | |
IsEvenInteger |
Returnerar sant om värdet är ett jämnt heltal. 2,0 returnerar true , och 2,2 returnerar false . |
|
IsFinite |
Returnerar sant om värdet inte är oändligt och inte NaN . |
|
IsImaginaryNumber |
Returnerar sant om värdet har en verklig nolldel. Det betyder att 0 det är imaginärt och 1 + 1i inte. |
|
IsInfinity |
Returnerar sant om värdet representerar oändligheten. | |
IsInteger |
Returnerar sant om värdet är ett heltal. 2.0 och 3.0 returnerar true , och 2.2 och 3.1 returnerar false . |
|
IsNaN |
Returnerar true om värdet representerar NaN . |
|
IsNegative |
Returnerar sant om värdet är negativt. Detta inkluderar -0.0. | |
IsPositive |
Returnerar sant om värdet är positivt. Detta inkluderar 0 och +0.0. | |
IsRealNumber |
Returnerar sant om värdet har en noll imaginär del. Det innebär att 0 är verkligt, liksom alla INumber<T> typer. |
|
IsZero |
Returnerar sant om värdet representerar noll. Detta inkluderar 0, +0.0 och -0.0. | |
MaxMagnitude |
Returnerar värdet med ett större absolut värde och returnerar NaN om någon av indata är NaN . |
|
MaxMagnitudeNumber |
Returnerar värdet med ett större absolut värde och returnerar talet om en indata är NaN . |
|
MinMagnitude |
Returnerar värdet med ett mindre absolut värde och returnerar NaN om någon av indata är NaN . |
|
MinMagnitudeNumber |
Returnerar värdet med ett mindre absolut värde och returnerar talet om en indata är NaN . |
|
ISignedNumber<TSelf> | NegativeOne |
Hämtar värdet -1 för typen. |
1Tänk på följande exempel för att förstå beteendet för de tre Create*
metoderna.
Exempel när du får ett värde som är för stort:
byte.CreateChecked(384)
genererar en OverflowException.byte.CreateSaturating(384)
returnerar 255 eftersom 384 är större än Byte.MaxValue (vilket är 255).byte.CreateTruncating(384)
returnerar 128 eftersom den tar de lägsta 8 bitarna (384 har en hexrepresentation av0x0180
, och de lägsta 8 bitarna är0x80
, vilket är 128).
Exempel när du får ett värde som är för litet:
byte.CreateChecked(-384)
genererar en OverflowException.byte.CreateSaturating(-384)
returnerar 0 eftersom -384 är mindre än Byte.MinValue (vilket är 0).byte.CreateTruncating(-384)
returnerar 128 eftersom den tar de lägsta 8 bitarna (384 har en hexrepresentation av0xFE80
, och de lägsta 8 bitarna är0x80
, vilket är 128).
Metoderna Create*
har också några särskilda överväganden för IEEE 754-flyttalstyper som float
och double
, eftersom de har specialvärdena PositiveInfinity
, NegativeInfinity
och NaN
. Alla tre Create*
API:erna fungerar som CreateSaturating
. Även om MinValue
och MaxValue
representerar det största negativa/positiva "normala" talet, är NegativeInfinity
de faktiska lägsta och högsta värdena och PositiveInfinity
, så att de klämmer fast dessa värden i stället.
Operatorgränssnitt
Operatorgränssnitten motsvarar de olika operatorer som är tillgängliga för C#-språket.
- De parkopplar uttryckligen inte åtgärder som multiplikation och division eftersom det inte är korrekt för alla typer. Är till exempel
Matrix4x4 * Matrix4x4
giltigt, menMatrix4x4 / Matrix4x4
är inte giltigt. - De tillåter vanligtvis att indata- och resultattyperna skiljer sig åt för att stödja scenarier, till exempel att dela upp två heltal för att hämta ett
double
, till exempel3 / 2 = 1.5
, eller beräkna medelvärdet för en uppsättning heltal.
Gränssnittsnamn | Definierade operatorer |
---|---|
IAdditionOperators<TSelf,TOther,TResult> | x + y |
IBitwiseOperators<TSelf,TOther,TResult> | x & y , 'x | y', x ^ y , och ~x |
IComparisonOperators<TSelf,TOther,TResult> | x < y , x > y , x <= y och x >= y |
IDecrementOperators<TSelf> | --x och x-- |
IDivisionOperators<TSelf,TOther,TResult> | x / y |
IEqualityOperators<TSelf,TOther,TResult> | x == y och x != y |
IIncrementOperators<TSelf> | ++x och x++ |
IModulusOperators<TSelf,TOther,TResult> | x % y |
IMultiplyOperators<TSelf,TOther,TResult> | x * y |
IShiftOperators<TSelf,TOther,TResult> | x << y och x >> y |
ISubtractionOperators<TSelf,TOther,TResult> | x - y |
IUnaryNegationOperators<TSelf,TResult> | -x |
IUnaryPlusOperators<TSelf,TResult> | +x |
Kommentar
Vissa av gränssnitten definierar en kontrollerad operator utöver en vanlig okontrollerad operator. Kontrollerade operatorer anropas i kontrollerade kontexter och tillåter att en användardefinierad typ definierar överflödesbeteende. Om du implementerar en kontrollerad operator, till exempel , CheckedSubtraction(TSelf, TOther)måste du även implementera den avmarkerade operatorn, Subtraction(TSelf, TOther)till exempel .
Funktionsgränssnitt
Funktionsgränssnitten definierar vanliga matematiska API:er som tillämpas bredare än på ett specifikt numeriskt gränssnitt. Alla dessa gränssnitt implementeras av IFloatingPointIeee754<TSelf>och kan implementeras av andra relevanta typer i framtiden.
Gränssnittsnamn | beskrivning |
---|---|
IExponentialFunctions<TSelf> | Exponerar exponentiella funktioner som stöder e^x , e^x - 1 , 2^x , 2^x - 1 , 10^x och 10^x - 1 . |
IHyperbolicFunctions<TSelf> | Exponerar hyperboliska funktioner som stöder acosh(x) , asinh(x) , atanh(x) , cosh(x) , sinh(x) och tanh(x) . |
ILogarithmicFunctions<TSelf> | Exponerar logaritmiska funktioner som stöder ln(x) , ln(x + 1) , log2(x) , log2(x + 1) , log10(x) och log10(x + 1) . |
IPowerFunctions<TSelf> | Exponerar energifunktioner som stöder x^y . |
IRootFunctions<TSelf> | Exponerar stöd för rotfunktioner cbrt(x) och sqrt(x) . |
ITrigonometricFunctions<TSelf> | Exponerar trigonometriska funktioner som stöder acos(x) , asin(x) , atan(x) , cos(x) , sin(x) och tan(x) . |
Parsnings- och formateringsgränssnitt
Parsning och formatering är grundläggande begrepp inom programmering. De används ofta när användaren konverterar indata till en viss typ eller visar en typ för användaren. Dessa gränssnitt finns i System namnområdet.
Gränssnittsnamn | beskrivning |
---|---|
IParsable<TSelf> | Exponerar stöd för T.Parse(string, IFormatProvider) och T.TryParse(string, IFormatProvider, out TSelf) . |
ISpanParsable<TSelf> | Exponerar stöd för T.Parse(ReadOnlySpan<char>, IFormatProvider) och T.TryParse(ReadOnlySpan<char>, IFormatProvider, out TSelf) . |
IFormattable1 | Exponerar stöd för value.ToString(string, IFormatProvider) . |
ISpanFormattable1 | Exponerar stöd för value.TryFormat(Span<char>, out int, ReadOnlySpan<char>, IFormatProvider) . |
1Det här gränssnittet är inte nytt och är inte heller allmänt. Den implementeras dock av alla nummertyper och representerar inverteringsåtgärden för IParsable
.
Följande program tar till exempel två tal som indata och läser dem från konsolen med en allmän metod där typparametern är begränsad till IParsable<TSelf>. Det beräknar medelvärdet med en allmän metod där typparametrarna för indata- och resultatvärdena är begränsade till , INumber<TSelf>och visar sedan resultatet för konsolen.
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
*/