Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Anggota virtual statis antarmuka memungkinkan Anda menentukan antarmuka yang mencakup operator yang kelebihan beban atau anggota statis lainnya. Setelah menentukan antarmuka dengan anggota statis, Anda dapat menggunakan antarmuka tersebut sebagai batasan untuk membuat jenis generik yang menggunakan operator atau metode statis lainnya. Bahkan jika Anda tidak membuat antarmuka dengan operator yang kelebihan beban, Anda mungkin mendapat manfaat dari fitur ini dan kelas matematika generik yang diaktifkan oleh pembaruan bahasa.
Dalam tutorial ini, Anda akan belajar cara:
- Tentukan antarmuka dengan anggota statis.
- Gunakan antarmuka untuk menentukan kelas yang mengimplementasikan antarmuka dengan operator yang ditentukan.
- Buat algoritma generik yang mengandalkan metode antarmuka statis.
Prasyarat
Metode antarmuka abstrak statis
Mari kita mulai dengan contoh. Metode berikut mengembalikan titik tengah dari dua double angka:
public static double MidPoint(double left, double right) =>
(left + right) / (2.0);
Logika yang sama akan berfungsi untuk jenis numerik apa pun: int, , short, longfloatdecimal, atau jenis apa pun yang mewakili angka. Anda harus memiliki cara untuk menggunakan + operator dan / , dan untuk menentukan nilai untuk 2. Anda dapat menggunakan System.Numerics.INumber<TSelf> antarmuka untuk menulis metode sebelumnya sebagai metode generik berikut:
public static T MidPoint<T>(T left, T right)
where T : INumber<T> => (left + right) / T.CreateChecked(2); // note: the addition of left and right may overflow here; it's just for demonstration purposes
Jenis apa pun yang mengimplementasikan INumber<TSelf> antarmuka harus menyertakan definisi untuk operator +, dan untuk operator /. Denominator didefinisikan oleh T.CreateChecked(2) untuk membuat nilai 2 untuk jenis numerik apa pun, yang memaksa denominator menjadi jenis yang sama dengan dua parameter.
INumberBase<TSelf>.CreateChecked<TOther>(TOther) membuat instans jenis dari nilai yang ditentukan dan melemparkan OverflowException jika nilai berada di luar rentang yang dapat diwakili. (Implementasi ini memiliki potensi luapan jika left dan right keduanya merupakan nilai yang cukup besar. Ada algoritma alternatif yang dapat menghindari potensi masalah ini.)
Anda menentukan anggota abstrak statis dalam antarmuka menggunakan sintaks yang familier: Anda menambahkan static pengubah dan abstract ke anggota statis apa pun yang tidak memberikan implementasi. Contoh berikut mendefinisikan IGetNext<T> antarmuka yang dapat diterapkan ke jenis apa pun yang mengambil alih operator ++:
public interface IGetNext<T> where T : IGetNext<T>
{
static abstract T operator ++(T other);
}
Batasan bahwa argumen jenis, T, menerapkan IGetNext<T> memastikan bahwa tanda tangan untuk operator menyertakan jenis yang berisi, atau argumen jenisnya. Banyak operator memberlakukan bahwa parameternya harus cocok dengan tipe, atau menjadi parameter tipe yang dibatasi untuk menerapkan tipe yang mencakup. Tanpa batasan ini, ++ operator tidak dapat didefinisikan dalam IGetNext<T> antarmuka.
Anda dapat membuat struktur yang membuat string karakter 'A' di mana setiap kenaikan menambahkan karakter lain ke string menggunakan kode berikut:
public struct RepeatSequence : IGetNext<RepeatSequence>
{
private const char Ch = 'A';
public string Text = new string(Ch, 1);
public RepeatSequence() {}
public static RepeatSequence operator ++(RepeatSequence other)
=> other with { Text = other.Text + Ch };
public override string ToString() => Text;
}
Secara umum, Anda dapat membangun algoritma apa pun di mana Anda mungkin ingin mendefinisikan ++ untuk berarti "menghasilkan nilai berikutnya dari jenis ini." Penggunaan antarmuka ini menghasilkan kode dan hasil yang mudah dipahami.
var str = new RepeatSequence();
for (int i = 0; i < 10; i++)
Console.WriteLine(str++);
Contoh sebelumnya menghasilkan output berikut:
A
AA
AAA
AAAA
AAAAA
AAAAAA
AAAAAAA
AAAAAAAA
AAAAAAAAA
AAAAAAAAAA
Contoh kecil ini menunjukkan motivasi untuk fitur ini. Anda dapat menggunakan sintaksis alami untuk operator, nilai konstanta, dan operasi statis lainnya. Anda dapat menjelajahi teknik ini saat membuat beberapa jenis yang mengandalkan anggota statis, termasuk operator yang kelebihan beban. Tentukan antarmuka yang cocok dengan kemampuan jenis Anda lalu deklarasikan dukungan jenis tersebut untuk antarmuka baru.
Matematika generik
Skenario memotivasi untuk memungkinkan metode statis, termasuk operator, dalam antarmuka adalah untuk mendukung algoritma matematika generik . Pustaka kelas dasar .NET 7 berisi definisi antarmuka untuk banyak operator aritmatika, dan antarmuka turunan yang menggabungkan banyak operator aritmatika dalam INumber<T> antarmuka. Mari kita terapkan jenis tersebut untuk membangun catatan Point<T> yang dapat memanfaatkan jenis numerik apapun sebagai T. Anda dapat memindahkan titik dengan beberapa XOffset dan YOffset menggunakan + operator.
Mulailah dengan membuat aplikasi Konsol baru, baik dengan menggunakan dotnet new atau Visual Studio.
Antarmuka publik untuk Translation<T> dan Point<T> akan terlihat seperti kode berikut:
// Note: Not complete. This won't compile yet.
public record Translation<T>(T XOffset, T YOffset);
public record Point<T>(T X, T Y)
{
public static Point<T> operator +(Point<T> left, Translation<T> right);
}
Anda menggunakan record untuk kedua tipe Translation<T> dan Point<T>: Keduanya menyimpan dua nilai, dan mewakili penyimpanan data daripada perilaku kompleks. Implementasi operator + akan terlihat seperti kode berikut:
public static Point<T> operator +(Point<T> left, Translation<T> right) =>
left with { X = left.X + right.XOffset, Y = left.Y + right.YOffset };
Agar kode sebelumnya dikompilasi, Anda perlu mendeklarasikan bahwa T mendukung IAdditionOperators<TSelf, TOther, TResult> antarmuka. Antarmuka tersebut mencakup metode yang bersifat statis operator +. Ini mendeklarasikan tiga parameter tipe: Satu untuk operand kiri, satu untuk operand kanan, dan satu untuk hasilnya. Beberapa jenis mengimplementasikan + untuk jenis operand dan hasil yang berbeda. Tambahkan deklarasi bahwa argumen jenis, T mengimplementasikan IAdditionOperators<T, T, T>:
public record Point<T>(T X, T Y) where T : IAdditionOperators<T, T, T>
Setelah Anda menambahkan batasan tersebut, kelas Point<T> Anda dapat menggunakan + untuk operator tambahannya. Tambahkan batasan yang sama pada Translation<T> deklarasi:
public record Translation<T>(T XOffset, T YOffset) where T : IAdditionOperators<T, T, T>;
Batasan IAdditionOperators<T, T, T> mencegah pengembang menggunakan kelas Anda untuk membuat Translation dengan jenis yang tidak memenuhi batasan untuk penambahan pada titik. Anda menambahkan batasan yang diperlukan ke parameter jenis untuk Translation<T> dan Point<T> agar kode ini berfungsi. Anda dapat menguji dengan menambahkan kode seperti berikut di atas deklarasi Translation dan Point dalam file Program.cs Anda:
var pt = new Point<int>(3, 4);
var translate = new Translation<int>(5, 10);
var final = pt + translate;
Console.WriteLine(pt);
Console.WriteLine(translate);
Console.WriteLine(final);
Anda dapat membuat kode ini lebih dapat digunakan kembali dengan menyatakan bahwa jenis ini mengimplementasikan antarmuka aritmatika yang sesuai. Perubahan pertama yang harus dilakukan adalah mendeklarasikan yang Point<T, T> mengimplementasikan IAdditionOperators<Point<T>, Translation<T>, Point<T>> antarmuka. Jenis Point ini menggunakan tipe yang berbeda untuk operand dan hasilnya. Tipe sudah Point mengimplementasikan operator + dengan tanda tangan tersebut, jadi dengan menambahkan antarmuka ke dalam deklarasi sudah semua yang Anda butuhkan:
public record Point<T>(T X, T Y) : IAdditionOperators<Point<T>, Translation<T>, Point<T>>
where T : IAdditionOperators<T, T, T>
Terakhir, saat Anda melakukan penambahan, ada baiknya untuk memiliki properti yang menentukan nilai identitas aditif untuk jenis tersebut. Ada antarmuka baru untuk fitur tersebut: IAdditiveIdentity<TSelf,TResult>. Terjemahan dari {0, 0} adalah identitas aditif: Titik hasilnya sama dengan operand kiri. Antarmuka IAdditiveIdentity<TSelf, TResult> mendefinisikan satu properti readonly, AdditiveIdentity, yang mengembalikan nilai identitas.
Translation<T> membutuhkan beberapa perubahan untuk mengimplementasikan antarmuka ini.
using System.Numerics;
public record Translation<T>(T XOffset, T YOffset) : IAdditiveIdentity<Translation<T>, Translation<T>>
where T : IAdditionOperators<T, T, T>, IAdditiveIdentity<T, T>
{
public static Translation<T> AdditiveIdentity =>
new Translation<T>(XOffset: T.AdditiveIdentity, YOffset: T.AdditiveIdentity);
}
Ada beberapa perubahan di sini, jadi mari kita berjalan melalui mereka satu per satu. Pertama, Anda menyatakan bahwa tipe Translation mengimplementasikan antarmuka IAdditiveIdentity:
public record Translation<T>(T XOffset, T YOffset) : IAdditiveIdentity<Translation<T>, Translation<T>>
Anda selanjutnya dapat mencoba menerapkan anggota antarmuka seperti yang ditunjukkan dalam kode berikut:
public static Translation<T> AdditiveIdentity =>
new Translation<T>(XOffset: 0, YOffset: 0);
Kode sebelumnya tidak dikompilasi, karena 0 tergantung pada jenisnya. Jawabannya: Gunakan IAdditiveIdentity<T>.AdditiveIdentity untuk 0. Perubahan itu berarti bahwa batasan Anda sekarang harus menyertakan bahwa T mengimplementasikan IAdditiveIdentity<T>. Hal ini menghasilkan implementasi berikut:
public static Translation<T> AdditiveIdentity =>
new Translation<T>(XOffset: T.AdditiveIdentity, YOffset: T.AdditiveIdentity);
Sekarang setelah Anda menambahkan batasan tersebut pada Translation<T>, Anda perlu menambahkan batasan yang sama ke Point<T>:
using System.Numerics;
public record Point<T>(T X, T Y) : IAdditionOperators<Point<T>, Translation<T>, Point<T>>
where T : IAdditionOperators<T, T, T>, IAdditiveIdentity<T, T>
{
public static Point<T> operator +(Point<T> left, Translation<T> right) =>
left with { X = left.X + right.XOffset, Y = left.Y + right.YOffset };
}
Sampel ini menunjukkan kepada Anda bagaimana antarmuka untuk matematika generik disusun. Anda mempelajari cara untuk:
- Tulis metode yang bergantung pada
INumber<T>antarmuka sehingga metode tersebut dapat digunakan dengan jenis numerik apa pun. - Buat jenis yang bergantung pada antarmuka penambahan untuk mengimplementasikan jenis yang hanya mendukung satu operasi matematika. Jenis itu mendeklarasikan dukungannya untuk antarmuka yang sama sehingga dapat disusun dengan cara lain. Algoritma ditulis menggunakan sintaks operator matematika yang paling alami.
Bereksperimenlah dengan fitur-fitur ini dan daftarkan umpan balik. Anda dapat menggunakan item menu Kirim Umpan Balik di Visual Studio, atau membuat masalah baru di repositori roslyn di GitHub. Buat algoritma generik yang berfungsi dengan jenis numerik apa pun. Buat algoritma menggunakan antarmuka ini di mana argumen jenis hanya mengimplementasikan subset kemampuan seperti angka. Bahkan jika Anda tidak membangun antarmuka baru yang menggunakan kemampuan ini, Anda dapat bereksperimen menggunakannya dalam algoritma Anda.