Bagikan melalui


dimana (batasan jenis generik) - (Referensi C#)

Klausa where di dalam definisi generik menentukan batasan pada jenis yang digunakan sebagai argumen untuk parameter jenis dalam jenis generik, metode, delegasi, atau fungsi lokal. Batasan bisa menentukan antarmuka, kelas dasar, atau memerlukan jenis generik untuk menjadi jenis referensi, nilai, atau tidak terkelola. Mereka mendeklarasikan kemampuan yang harus dimiliki argumen jenis, dan harus ditempatkan setelah kelas dasar yang dideklarasikan atau antarmuka yang diimplementasikan.

Misalnya, Anda bisa mendeklarasikan kelas generik, AGenericClass, sehingga parameter T jenis mengimplementasikan IComparable<T> antarmuka:

public class AGenericClass<T> where T : IComparable<T> { }

Catatan

Untuk informasi selengkapnya tentang klausul where dalam ekspresi kueri, lihatlah klausul where.

Klausul where juga bisa mencakup batasan kelas dasar. Batasan kelas dasar menyatakan bahwa jenis yang akan digunakan sebagai argumen jenis untuk jenis generik tersebut memiliki kelas yang ditentukan sebagai kelas dasar, atau merupakan kelas dasar tersebut. Jika batasan kelas dasar digunakan, batasan tersebut harus muncul sebelum batasan lainnya pada parameter jenis tersebut. Beberapa jenis tidak diizinkan sebagai batasan kelas dasar: Object, Array, dan ValueType. Contoh berikut ini menunjukkan jenis yang sekarang dapat ditentukan sebagai kelas dasar:

public class UsingEnum<T> where T : System.Enum { }

public class UsingDelegate<T> where T : System.Delegate { }

public class Multicaster<T> where T : System.MulticastDelegate { }

Dalam konteks nullable, nullability dari jenis kelas dasar diberlakukan. Jika kelas dasar tidak bisa diubah ke null (misalnya Base), argumen jenis harus tidak dapat diubah ke null. Jika kelas dasar dapat diubah ke null (misalnya Base?), argumen jenis dapat berupa jenis referensi nullable atau non-nullable. Pengkompilasi memberikan peringatan jika argumen jenis adalah jenis referensi yang dapat diubah ke null saat kelas dasar tidak dapat diubah ke null.

Klausa where bisa menentukan bahwa jenisnya adalah class atau struct. Batasan struct menghapus kebutuhan untuk menentukan batasan kelas dasar System.ValueType. System.ValueType Jenis tidak dapat digunakan sebagai batasan kelas dasar. Contoh berikut ini menunjukkan batasan class dan struct :

class MyClass<T, U>
    where T : class
    where U : struct
{ }

Dalam konteks nullable, class batasan mengharuskan jenis menjadi jenis referensi yang tidak dapat diubah ke null. Untuk mengizinkan jenis referensi yang dapat diubah ke null, gunakan class? batasan, yang memungkinkan jenis referensi yang dapat diubah ke null dan yang tidak dapat diubah ke null.

Klausa where dapat mencakup batasan notnull . Batasan notnull membatasi parameter jenis ke jenis yang tidak dapat diubah ke null. Jenis dapat berupa jenis nilai atau jenis referensi yang tidak dapat diubah ke null. Batasan notnull tersedia untuk kode yang nullable enable dikompilasi dalam konteks. Tidak seperti batasan lainnya, jika argumen jenis melanggar batasan notnull, pengompilasi membuat peringatan alih-alih kesalahan. Peringatan hanya dihasilkan dalam konteks nullable enable.

Penambahan jenis referensi yang dapat diubah ke null memperkenalkan ambiguitas potensial dalam arti T? dalam metode generik. Jika T adalah struct, maka T? sama System.Nullable<T>dengan. Namun, jika T merupakan jenis referensi, T? berarti itu adalah nilai yang null valid. Ambiguitas hadir karena metode penimpaan tidak dapat mencakup batasan. Batasan default baru menyelesaikan ambiguitas ini. Anda menambahkannya ketika kelas dasar atau antarmuka mendeklarasikan dua kelebihan beban metode, satu yang menentukan struct batasan, dan yang tidak memiliki struct batasan atau class diterapkan:

public abstract class B
{
    public void M<T>(T? item) where T : struct { }
    public abstract void M<T>(T? item);

}

Anda menggunakan batasan default untuk menentukan bahwa kelas turunan Anda mengambil alih metode tanpa batasan di kelas turunan Anda, atau implementasi antarmuka eksplisit. Ini hanya akan valid pada metode yang mengambil alih metode dasar, atau implementasi antarmuka eksplisit:

public class D : B
{
    // Without the "default" constraint, the compiler tries to override the first method in B
    public override void M<T>(T? item) where T : default { }
}

Penting

Deklarasi generik yang menyertakan notnull batasan dapat digunakan dalam konteks tidak sadar null, tetapi kompilator tidak memberlakukan batasan.

#nullable enable
    class NotNullContainer<T>
        where T : notnull
    {
    }
#nullable restore

Klausa where juga dapat menyertakan batasan unmanaged . Batasan unmanaged membatasi parameter jenis ke jenis yang dikenal sebagai jenis yang belum dikelola. Batasan unmanaged memudahkan untuk menulis kode interop tingkat rendah dalam C#. Batasan ini memungkinkan rutinitas yang bisa digunakan kembali di semua jenis yang tidak dikelola. Batasan unmanaged tidak dapat digabungkan dengan batasan class atau struct. Batasan unmanaged memaksa bahwa tipenya harus berupa struct:

class UnManagedWrapper<T>
    where T : unmanaged
{ }

Klausul ini where juga dapat mencakup batasan konstruktor, new(). Batasan itu memungkinkan untuk membuat instance parameter tipe menggunakan operator new. Batasan new() memberi tahu kompiler bahwa argumen tipe apa pun yang diberikan harus memiliki konstruktor tanpa parameter yang dapat diakses. Contohnya:

public class MyGenericClass<T> where T : IComparable<T>, new()
{
    // The following line is not possible without new() constraint:
    T item = new T();
}

Batasan new() muncul terakhir dalam where klausul, kecuali diikuti oleh allows ref struct anti-batasan. Batasan new() tidak dapat digabungkan dengan batasan struct atau unmanaged. Semua tipe yang memenuhi batasan tersebut harus memiliki konstruktor tanpa parameter yang dapat diakses, membuat batasan new() menjadi berlebihan.

Anti-batasan ini menyatakan bahwa argumen jenis untuk T bisa menjadi ref struct jenis. Contohnya:

public class GenericRefStruct<T> where T : allows ref struct
{
    // Scoped is allowed because T might be a ref struct
    public void M(scoped T parm)
    {

    }
}

Jenis atau metode generik harus mematuhi aturan keamanan ref untuk instans T apa pun karena mungkin berupa ref struct. Klausa allows ref struct tidak dapat dikombinasikan dengan batasan class atau class? . Anti-batasan allows ref struct harus mengikuti semua batasan untuk argumen jenis tersebut.

Dengan beberapa parameter tipe, gunakan satu klausul where untuk setiap parameter tipe, misalnya:

public interface IMyInterface { }

namespace CodeExample
{
    class Dictionary<TKey, TVal>
        where TKey : IComparable<TKey>
        where TVal : IMyInterface
    {
        public void Add(TKey key, TVal val) { }
    }
}

Anda juga dapat melampirkan batasan ke parameter tipe metode generik, seperti yang ditunjukkan pada contoh berikut:

public void MyMethod<T>(T t) where T : IMyInterface { }

Perhatikan bahwa sintaks untuk menjelaskan batasan parameter tipe pada delegasi sama dengan sintaksis metode:

delegate T MyDelegate<T>() where T : new();

Untuk informasi tentang delegasi generik, lihatlah Delegasi Generik.

Untuk detail tentang sintaksis dan penggunaan batasan, lihatlah Batasan pada Parameter Jenis.

Spesifikasi bahasa C#

Untuk informasi selengkapnya, lihat Spesifikasi Bahasa C#. Spesifikasi bahasa adalah sumber definitif untuk sintaks dan penggunaan C#.

Lihat juga