Baca dalam bahasa Inggris

Bagikan melalui


Matematika generik

.NET 7 memperkenalkan antarmuka umum terkait matematika baru ke pustaka kelas dasar. Ketersediaan antarmuka ini berarti Anda dapat membatasi parameter jenis jenis atau metode generik menjadi "seperti angka". Selain itu, C# 11 dan yang lebih baru memungkinkan Anda menentukan static virtual anggota antarmuka. Karena operator harus dinyatakan sebagai static, fitur C# baru ini memungkinkan operator dideklarasikan dalam antarmuka baru untuk jenis seperti angka.

Bersama-sama, inovasi ini memungkinkan Anda untuk melakukan operasi matematika secara generis—yaitu, tanpa harus mengetahui jenis yang tepat yang sedang Anda kerjakan. Misalnya, jika Anda ingin menulis metode yang menambahkan dua angka, sebelumnya Anda harus menambahkan kelebihan metode untuk setiap jenis (misalnya, static int Add(int first, int second) dan static float Add(float first, float second)). Sekarang Anda dapat menulis metode umum tunggal, di mana parameter jenis dibatasi menjadi jenis seperti angka. Contohnya:

static T Add<T>(T left, T right)
    where T : INumber<T>
{
    return left + right;
}

Dalam metode ini, parameter T jenis dibatasi menjadi jenis yang mengimplementasikan antarmuka baru INumber<TSelf> . INumber<TSelf>IAdditionOperators<TSelf,TOther,TResult> mengimplementasikan antarmuka, yang berisi operator + . Itu memungkinkan metode untuk secara generik menambahkan dua angka. Metode ini dapat digunakan dengan salah satu dari . Jenis numerik bawaan NET, karena semuanya telah diperbarui untuk diterapkan INumber<TSelf> di .NET 7.

Penulis pustaka akan mendapat manfaat paling besar dari antarmuka matematika generik, karena mereka dapat menyederhanakan basis kode mereka dengan menghapus kelebihan beban "redundan". Pengembang lain akan mendapat manfaat secara tidak langsung, karena API yang mereka konsumsi dapat mulai mendukung lebih banyak jenis.

Antarmuka

Antarmuka dirancang agar cukup terperinci sehingga pengguna dapat menentukan antarmuka mereka sendiri di atasnya, sekaligus cukup terperinci sehingga mudah dikonsumsi. Sejauh itu, ada beberapa antarmuka numerik inti yang akan berinteraksi dengan sebagian besar pengguna, seperti INumber<TSelf> dan IBinaryInteger<TSelf>. Antarmuka yang lebih halus, seperti IAdditionOperators<TSelf,TOther,TResult> dan ITrigonometricFunctions<TSelf>, mendukung jenis ini dan tersedia untuk pengembang yang menentukan antarmuka numerik khusus domain mereka sendiri.

Antarmuka numerik

Bagian ini menjelaskan antarmuka dalam System.Numerics yang menjelaskan jenis seperti angka dan fungsionalitas yang tersedia untuknya.

Nama antarmuka Deskripsi
IBinaryFloatingPointIeee754<TSelf> Mengekspos API yang umum untuk biner floating-point jenis1 yang mengimplementasikan standar IEEE 754.
IBinaryInteger<TSelf> Mengekspos API yang umum untuk bilanganbulat biner 2.
IBinaryNumber<TSelf> Mengekspos API umum ke angka biner.
IFloatingPoint<TSelf> Mengekspos API yang umum untuk jenis floating-point.
IFloatingPointIeee754<TSelf> Mengekspos API yang umum untuk jenis floating-point yang mengimplementasikan standar IEEE 754.
INumber<TSelf> Mengekspos API yang umum untuk jenis angka yang sebanding (secara efektif domain angka "nyata").
INumberBase<TSelf> Mengekspos API umum ke semua jenis angka (secara efektif domain angka "kompleks").
ISignedNumber<TSelf> Mengekspos API yang umum untuk semua jenis angka yang ditandatangani (seperti konsep NegativeOne).
IUnsignedNumber<TSelf> Mengekspos API umum ke semua jenis angka yang tidak ditandatangani.
IAdditiveIdentity<TSelf,TResult> Mengekspos konsep (x + T.AdditiveIdentity) == x.
IMinMaxValue<TSelf> Mengekspos konsep T.MinValue dan T.MaxValue.
IMultiplicativeIdentity<TSelf,TResult> Mengekspos konsep (x * T.MultiplicativeIdentity) == x.

1Jenis titik mengambang biner adalah Double (double), Half, dan Single (float).

2Jenis bilangan bulat biner adalah Byte (byte), (), Int16 (short), Int32 (intlong), Int128Int64 , IntPtr (nint), SByte (), (ushortsbyte), UInt16 (), UInt32 (uint), (), UInt64 (ulong), UInt128, dan UIntPtr ().nuint

Antarmuka yang paling mungkin Anda gunakan secara langsung adalah INumber<TSelf>, yang kira-kira sesuai dengan angka riil . Jika jenis mengimplementasikan antarmuka ini, itu berarti bahwa nilai memiliki tanda (ini termasuk unsigned jenis, yang dianggap positif) dan dapat dibandingkan dengan nilai lain dengan jenis yang sama. INumberBase<TSelf> menganugerah konsep yang lebih canggih, seperti bilangan kompleks dan imajiner , misalnya, akar kuadrat dari angka negatif. Antarmuka lain, seperti IFloatingPointIeee754<TSelf>, dibuat karena tidak semua operasi masuk akal untuk semua jenis angka—misalnya, menghitung lantai angka hanya masuk akal untuk jenis floating-point. Di pustaka kelas dasar .NET, jenis Double floating-point mengimplementasikan IFloatingPointIeee754<TSelf> tetapi Int32 tidak.

Beberapa antarmuka juga diimplementasikan oleh berbagai jenis lain, termasuk Char, , DateOnly, DateTimeDateTimeOffset, Decimal, Guid, TimeOnly, , dan TimeSpan.

Tabel berikut menunjukkan beberapa API inti yang diekspos oleh setiap antarmuka.

Antarmuka Nama API Deskripsi
IBinaryInteger<TSelf> DivRem Menghitung kuota dan sisanya secara bersamaan.
LeadingZeroCount Menghitung jumlah bit nol di depannya dalam representasi biner.
PopCount Menghitung jumlah bit yang ditetapkan dalam representasi biner.
RotateLeft Memutar bit ke kiri, kadang-kadang juga disebut shift kiri melingkar.
RotateRight Memutar bit ke kanan, kadang-kadang juga disebut shift kanan melingkar.
TrailingZeroCount Menghitung jumlah bit nol berikutnya dalam representasi biner.
IFloatingPoint<TSelf> Ceiling Membulatkan nilai ke arah tak terbatas positif. +4.5 menjadi +5, dan -4.5 menjadi -4.
Floor Membulatkan nilai ke arah tak terbatas negatif. +4.5 menjadi +4, dan -4.5 menjadi -5.
Round Membulatkan nilai menggunakan mode pembulatan yang ditentukan.
Truncate Membulatkan nilai menuju nol. +4.5 menjadi +4, dan -4.5 menjadi -4.
IFloatingPointIeee754<TSelf> E Mendapatkan nilai yang mewakili nomor Euler untuk jenis tersebut.
Epsilon Mendapatkan nilai terkecil yang dapat diwakili yang lebih besar dari nol untuk jenis tersebut.
NaN Mendapatkan nilai yang mewakili NaN jenis tersebut.
NegativeInfinity Mendapatkan nilai yang mewakili -Infinity jenis tersebut.
NegativeZero Mendapatkan nilai yang mewakili -Zero jenis tersebut.
Pi Mendapatkan nilai yang mewakili Pi jenis tersebut.
PositiveInfinity Mendapatkan nilai yang mewakili +Infinity jenis tersebut.
Tau Mendapatkan nilai yang mewakili Tau (2 * Pi) untuk jenis tersebut.
(Lainnya) (Mengimplementasikan set lengkap antarmuka yang tercantum di bawah Antarmuka fungsi.)
INumber<TSelf> Clamp Membatasi nilai untuk tidak lebih dan tidak kurang dari nilai min dan maks yang ditentukan.
CopySign Mengatur tanda nilai yang ditentukan sama dengan nilai lain yang ditentukan.
Max Mengembalikan nilai yang lebih besar dari dua nilai, mengembalikan NaN jika salah satu input adalah NaN.
MaxNumber Mengembalikan nilai yang lebih besar dari dua nilai, mengembalikan angka jika satu input adalah NaN.
Min Mengembalikan lebih sedikit dari dua nilai, mengembalikan NaN jika salah satu input adalah NaN.
MinNumber Mengembalikan lebih kecil dari dua nilai, mengembalikan angka jika satu input adalah NaN.
Sign Mengembalikan -1 untuk nilai negatif, 0 untuk nol, dan +1 untuk nilai positif.
INumberBase<TSelf> One Mendapatkan nilai 1 untuk jenis tersebut.
Radix Mendapatkan radix, atau dasar, untuk jenisnya. Int32 mengembalikan 2. Decimal mengembalikan 10.
Zero Mendapatkan nilai 0 untuk jenis tersebut.
CreateChecked Membuat nilai, melemparkan OverflowException jika input tidak dapat cocok.1
CreateSaturating Membuat nilai, menjepit ke T.MinValue atau T.MaxValue jika input tidak cocok.1
CreateTruncating Membuat nilai dari nilai lain, membungkus jika input tidak dapat pas.1
IsComplexNumber Mengembalikan true jika nilai memiliki bagian riil bukan nol dan bagian imajiner bukan nol.
IsEvenInteger Mengembalikan true jika nilainya adalah bilangan bulat genap. 2.0 mengembalikan true, dan 2,2 mengembalikan false.
IsFinite Mengembalikan true jika nilainya tidak tak terbatas dan bukan NaN.
IsImaginaryNumber Mengembalikan true jika nilai memiliki bagian riil nol. Ini berarti 0 adalah imajiner dan 1 + 1i tidak.
IsInfinity Mengembalikan true jika nilai mewakili tak terbatas.
IsInteger Mengembalikan true jika nilainya adalah bilangan bulat. 2.0 dan 3.0 mengembalikan true, dan 2,2 dan 3,1 mengembalikan false.
IsNaN Mengembalikan true jika nilai mewakili NaN.
IsNegative Mengembalikan true jika nilainya negatif. Ini termasuk -0.0.
IsPositive Mengembalikan true jika nilainya positif. Ini termasuk 0 dan +0.0.
IsRealNumber Mengembalikan true jika nilai memiliki bagian imajiner nol. Ini berarti 0 nyata seperti semua INumber<T> jenis.
IsZero Mengembalikan true jika nilai mewakili nol. Ini termasuk 0, +0.0, dan -0.0.
MaxMagnitude Mengembalikan nilai dengan nilai absolut yang lebih besar, mengembalikan NaN jika salah satu input adalah NaN.
MaxMagnitudeNumber Mengembalikan nilai dengan nilai absolut yang lebih besar, mengembalikan angka jika satu input adalah NaN.
MinMagnitude Mengembalikan nilai dengan nilai absolut yang lebih rendah, mengembalikan NaN jika salah satu input adalah NaN.
MinMagnitudeNumber Mengembalikan nilai dengan nilai absolut yang lebih kecil, mengembalikan angka jika satu input adalah NaN.
ISignedNumber<TSelf> NegativeOne Mendapatkan nilai -1 untuk jenis tersebut.

1Untuk membantu memahami perilaku tiga Create* metode, pertimbangkan contoh berikut.

Contoh saat diberi nilai yang terlalu besar:

  • byte.CreateChecked(384)akan melempar .OverflowException
  • byte.CreateSaturating(384) mengembalikan 255 karena 384 lebih besar dari Byte.MaxValue (yaitu 255).
  • byte.CreateTruncating(384) mengembalikan 128 karena membutuhkan 8 bit terendah (384 memiliki representasi heksa , 0x0180dan 8 bit terendah adalah 0x80, yaitu 128).

Contoh saat diberi nilai yang terlalu kecil:

  • byte.CreateChecked(-384)akan melempar .OverflowException
  • byte.CreateSaturating(-384) mengembalikan 0 karena -384 lebih kecil dari Byte.MinValue (yaitu 0).
  • byte.CreateTruncating(-384) mengembalikan 128 karena membutuhkan 8 bit terendah (384 memiliki representasi heksa , 0xFE80dan 8 bit terendah adalah 0x80, yaitu 128).

Metode ini Create* juga memiliki beberapa pertimbangan khusus untuk jenis floating-point IEEE 754, seperti float dan double, karena mereka memiliki nilai PositiveInfinitykhusus , , NegativeInfinitydan NaN. Ketiga Create* API berulah sebagai CreateSaturating. Selain itu, sementara MinValue dan MaxValue mewakili angka "normal" negatif/positif terbesar, nilai minimum dan maksimum aktual adalah NegativeInfinity dan PositiveInfinity, sehingga mereka menjepit nilai-nilai ini sebagai gantinya.

Antarmuka operator

Antarmuka operator sesuai dengan berbagai operator yang tersedia untuk bahasa C#.

  • Mereka secara eksplisit tidak memasangkan operasi seperti perkalian dan pembagian karena tidak benar untuk semua jenis. Misalnya, Matrix4x4 * Matrix4x4 valid, tetapi Matrix4x4 / Matrix4x4 tidak valid.
  • Mereka biasanya memungkinkan jenis input dan hasil berbeda untuk mendukung skenario seperti membagi dua bilangan bulat untuk mendapatkan double, misalnya, 3 / 2 = 1.5, atau menghitung rata-rata satu set bilangan bulat.

Catatan

Beberapa antarmuka menentukan operator yang diperiksa selain operator reguler yang tidak dicentang. Operator yang diperiksa dipanggil dalam konteks yang diperiksa dan memungkinkan jenis yang ditentukan pengguna untuk menentukan perilaku luapan. Jika Anda menerapkan operator yang diperiksa, misalnya, CheckedSubtraction(TSelf, TOther), Anda juga harus menerapkan operator yang tidak dicentang, misalnya, Subtraction(TSelf, TOther).

Antarmuka fungsi

Antarmuka fungsi menentukan API matematika umum yang berlaku lebih luas daripada ke antarmuka numerik tertentu. Antarmuka ini semuanya diimplementasikan oleh IFloatingPointIeee754<TSelf>, dan dapat diimplementasikan oleh jenis lain yang relevan di masa depan.

Nama antarmuka Deskripsi
IExponentialFunctions<TSelf> Mengekspos fungsi eksponensial yang mendukung e^x, , e^x - 1, 2^x2^x - 1, 10^x, dan 10^x - 1.
IHyperbolicFunctions<TSelf> Mengekspos fungsi hiperbolik yang acosh(x)mendukung , , asinh(x), atanh(x)cosh(x), sinh(x), dan tanh(x).
ILogarithmicFunctions<TSelf> Mengekspos fungsi logaritmik yang mendukung ln(x), , ln(x + 1), log2(x + 1)log2(x), log10(x), dan log10(x + 1).
IPowerFunctions<TSelf> Mengekspos fungsi daya yang x^ymendukung .
IRootFunctions<TSelf> Mengekspos fungsi akar yang cbrt(x) mendukung dan sqrt(x).
ITrigonometricFunctions<TSelf> Mengekspos fungsi trigonometrik yang acos(x)mendukung , , asin(x), cos(x)atan(x), sin(x), dan tan(x).

Mengurai dan memformat antarmuka

Penguraian dan pemformatan adalah konsep inti dalam pemrograman. Mereka biasanya digunakan saat mengonversi input pengguna ke jenis tertentu atau menampilkan jenis kepada pengguna. Antarmuka ini berada di System namespace layanan.

Nama antarmuka Deskripsi
IParsable<TSelf> Mengekspos dukungan untuk T.Parse(string, IFormatProvider) dan T.TryParse(string, IFormatProvider, out TSelf).
ISpanParsable<TSelf> Mengekspos dukungan untuk T.Parse(ReadOnlySpan<char>, IFormatProvider) dan T.TryParse(ReadOnlySpan<char>, IFormatProvider, out TSelf).
IFormattable1 Mengekspos dukungan untuk value.ToString(string, IFormatProvider).
ISpanFormattable1 Mengekspos dukungan untuk value.TryFormat(Span<char>, out int, ReadOnlySpan<char>, IFormatProvider).

1Antarmuka ini bukan baru, juga tidak umum. Namun, ini diimplementasikan oleh semua jenis angka dan mewakili operasi terbalik dari IParsable.

Misalnya, program berikut mengambil dua angka sebagai input, membacanya dari konsol menggunakan metode generik di mana parameter jenis dibatasi menjadi IParsable<TSelf>. Ini menghitung rata-rata menggunakan metode generik di mana parameter jenis untuk nilai input dan hasil dibatasi menjadi INumber<TSelf>, lalu menampilkan hasilnya ke konsol.

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
*/

Lihat juga