Mengapa ada batasan () baru di C # tetapi tidak ada kendala serupa lainnya?

19

Dalam C # generics, kita dapat mendeklarasikan batasan untuk tipe parameter Tuntuk memiliki konstruktor default, dengan mengatakan where T : new(). Namun, tidak ada kendala lain seperti ini yang valid - new(string)misalnya, dll.

Dari desain bahasa dan / atau perspektif implementasi, apa alasannya?

Apakah ada sesuatu dalam cara konstruktor bekerja atau dalam cara sistem tipe diimplementasikan yang melarang ini (atau setidaknya membuatnya lebih sulit)? Jika demikian, apakah itu? Saya ingat pernah membaca di suatu tempat yang default(T)sebenarnya cocok new T()untuk T : struct. Apakah ini terkait dengan ini, mungkin?

Atau itu hanya keputusan desain yang dibuat untuk menghindari membuat bahasa terlalu rumit?

Theodoros Chatzigiannakis
sumber
new(string)bukan kendala konstruktor default. Pertanyaan Anda sama saja dengan mengatakan "Mengapa tidak ada kendala yang memerlukan tanda tangan konstruktor tertentu?" Sangat mungkin karena kendala seperti itu tidak akan terlalu berguna.
Robert Harvey
3
@RobertHarvey Ya, itulah yang saya katakan. Saya pada dasarnya bertanya apakah ada sesuatu yang membuat konstruktor default khusus untuk implementasi atau apakah itu hanya pilihan sewenang-wenang untuk memasukkan ini dan bukan yang lain.
Theodoros Chatzigiannakis
Konstruktor default berguna dalam cara tertentu, dan penting, tertentu. Sebagai contoh, mereka membuat jenis mudah serial.
Robert Harvey
6
Tanda tangan aktor khusus dapat berguna. Misalnya, jika variabel tipe Anda dibatasi menjadi Koleksi <T> dengan ctor T (Koleksi <T>), Anda tahu Anda bisa membuat koleksi baru dengan yang lain. Namun, apakah kegunaan itu sepadan dengan kompleksitas ekstra, adalah sebuah pertanyaan.
Phoshi

Jawaban:

5

Untuk jawaban yang berwibawa, saya akan merujuk Anda ke jawaban Eric Lippert untuk pertanyaan itu di StackOverflow beberapa tahun yang lalu, sebuah cuplikan yang dikutip secara singkat di bawah ini.

Namun, dalam kasus khusus ini saya pasti dapat memberi Anda beberapa alasan mengapa saya akan mendorong kembali fitur tersebut jika muncul dalam rapat desain sebagai fitur yang mungkin untuk versi bahasa yang akan datang.

...

Saya mengatakan bahwa kita harus melakukan seluruh fitur atau tidak melakukannya sama sekali. Jika penting untuk dapat membatasi jenis untuk memiliki konstruktor tertentu, maka mari kita lakukan seluruh fitur dan membatasi jenis berdasarkan anggota secara umum dan bukan hanya konstruktor.

Chris Hannon
sumber
2
Kutipan itu, diambil di luar konteks, sangat menyesatkan. Ini menunjukkan bahwa Eric berpikir bahwa Microsoft seharusnya "pergi jauh-jauh," yang bukan posisinya sama sekali. Faktanya, dia benar-benar menentang fitur yang diusulkan.
Robert Harvey
1
Terima kasih, ini memang menjawab sebagian pertanyaan saya: Menjelang akhir jawabannya, Eric Lippert menyebutkan bahwa ini adalah batasan dari IL dan termasuk fitur ini akan membutuhkan tambahan ke IL. Itu akan menjadi sempurna jika Anda bisa menyediakan sumber pada apa yang dihasilkan IL untuk bagian yang ini dilaksanakan - yaitu, umum memanggil konstruktor default.
Theodoros Chatzigiannakis
@TheodorosChatzigiannakis: Mengapa Anda tidak menjalankan Dekompiler Telerik dan mencari tahu sendiri?
Robert Harvey
2
@RobertHarvey Saya tidak berpikir itu menunjukkan posisi untuknya sama sekali, tapi saya sudah memasukkan penawaran tambahan untuk menekankan posisinya.
Chris Hannon
1
Hmm, F # memiliki cara lebih lanjut untuk membatasi suatu tipe , seperti memeriksa waktu kompilasi jika suatu kelas memiliki operator. Mungkin, F # membutuhkan sistem kendala yang lebih kuat daripada C # karena pemeriksaan jenisnya yang sangat ketat. Meskipun demikian, bahasa ini mampu menerapkan cara-cara canggih untuk membatasi kelas pada .Net Framework.
OnesimusUnbound
16

Penguraian (sesuai saran Robert Harvey) menghasilkan yang berikut, untuk siapa pun yang tertarik. Metode ini:

static T GenericMake<T>()
    where T : new()
{
    return new T();
}

Rupanya, ketika dikompilasi, menjadi ini:

private static T GenericMake<T>()
    where T : new()
{
    T t;
    T t1 = default(T);
    if (t1 == null)
    {
        t = Activator.CreateInstance<T>();
    }
    else
    {
        t1 = default(T);
        t = t1;
    }
    return t;
}
  • Jika Tmerupakan tipe nilai, new()jadilah default(T).
  • Jika Tmerupakan tipe referensi, new()bekerja menggunakan refleksi. Activator.CreateInstance()panggilan internal RuntimeType.CreateInstanceDefaultCtor().

Jadi begitulah - secara internal, konstruktor default benar-benar khusus untuk C # dalam kaitannya dengan CLR. Memberikan konstruktor lain perlakuan yang sama akan mahal, bahkan jika ada beberapa kasus penggunaan yang valid untuk kendala yang lebih rumit dalam obat generik.

Theodoros Chatzigiannakis
sumber
4
Menarik. Mengapa panggilan duplikat ke default(T) untuk tipe nilai?
Avner Shahar-Kashtan
2
@ AvnerShahar-Kashtan saya tidak tahu. Ini mungkin merupakan artefak dari proses kompilasi / dekompilasi, seperti tvariabel (yang bisa diganti dengan returnpernyataan bersarang ).
Theodoros Chatzigiannakis