Pertanyaan Apakah sistem suara C # terdengar dan dapat ditentukan?


Saya tahu bahwa sistem tipe Java tidak sehat (gagal untuk memeriksa konstruk yang semantis legal) dan tidak dapat diputuskan (gagal untuk mengetik memeriksa beberapa konstruk).

Misalnya, jika Anda menyalin / menempelkan potongan berikut di kelas dan mengkompilasi, kompiler akan crash dengan StackOverflowException (seberapa tepat). Ini tidak dapat diputuskan.

static class ListX<T> {}
static class C<P> extends ListX<ListX<? super C<C<P>>>> {}
ListX<? super C<Byte>> crash = new C<Byte>();

Java menggunakan wildcard dengan batasan tipe, yang merupakan bentuk varians penggunaan-situs. C # di sisi lain, menggunakan anotasi anotasi situs deklarasi (dengan in dan out kata kunci). Diketahui bahwa varians situs-pernyataan lebih lemah daripada varians penggunaan-situs (varians use-site dapat menyatakan semua varian pernyataan-situs bisa, dan banyak lagi - di sisi bawah, itu jauh lebih verbose).

Jadi pertanyaan saya adalah: Apakah sistem C # type terdengar dan dapat ditentukan? Jika tidak, mengapa?


32
2018-05-29 17:20


asal


Jawaban:


Apakah sistem C # type dapat diuraikan?

Suatu sistem tipe adalah "decidable" jika compiler secara teori selalu dapat memutuskan apakah jenis program memeriksa atau tidak dalam waktu yang terbatas.

Itu tergantung pada batasan apa yang Anda berikan pada sistem jenis. Beberapa perancang sistem tipe C memiliki makalah tentang subjek yang mungkin Anda anggap menarik:

https://www.microsoft.com/en-us/research/publication/on-decidability-of-nominal-subtyping-with-variance/

Dalam prakteknya, kompiler C # 4.0 dan 5.0 tidak mengimplementasikan detektor jenis tidak berhingga yang dijelaskan di kertas; sebaliknya, mereka masuk ke rekursi yang tidak terbatas dan crash.

Saya mempertimbangkan untuk menambahkan kode tersebut ke Roslyn tetapi tidak ingat kali ini apakah itu masuk atau tidak; Saya akan memeriksa kode sumber ketika saya kembali ke kantor saya minggu depan.

Pengantar yang lebih lembut untuk masalah ini dapat ditemukan di artikel saya di sini:

http://blogs.msdn.com/b/ericlippert/archive/2008/05/07/covariance-and-contravariance-part-twelve-to-infinity-but-not-beyond.aspx

PEMBARUAN: Pertanyaan yang ditanyakan oleh Andrew di koran asli - apakah pengecekan konvertibilitas di dunia dengan subtyping nominal dan obat generik contravarian yang dapat diputuskan secara umum? - baru-baru ini dijawab. Bukan itu. Lihat https://arxiv.org/abs/1605.05274. Saya senang untuk dicatat bahwa penulis memperhatikan salah satu posting saya tentang hal ini - bukan yang ini - dan terinspirasi untuk menyerang masalah.

PEMBARUAN: Seorang komentator menunjukkan bahwa saya menjawab pertanyaan tentang ketegasan tetapi tidak sehat.

Apakah suara sistem C # type?

Sebuah sistem tipe adalah "suara" jika kita dijamin bahwa program jenis yang memeriksa pada waktu kompilasi tidak memiliki kesalahan ketik pada saat runtime.

Tidak, sistem tipe C # tidak bersuara, berkat kovarian array, fitur paling tidak favorit saya:

Giraffe[] giraffes = new[] { new Giraffe() };
Animal[] animals = giraffes; // This is legal!
animals[0] = new Tiger(); // crashes at runtime with a type error

Idenya di sini adalah bahwa sebagian besar metode yang mengambil array hanya membaca array, mereka tidak menulisnya, dan aman untuk membaca binatang dari berbagai macam jerapah. Java memungkinkan ini, dan CLR memungkinkan karena para desainer CLR ingin dapat menerapkan variasi di Java. C # memungkinkannya karena CLR mengizinkannya. Konsekuensinya adalah itu setiap kali Anda menulis sesuatu ke dalam array kelas dasar, runtime harus melakukan pemeriksaan untuk memverifikasi bahwa array bukan array dari kelas turunan yang tidak kompatibel. Kasus umum menjadi lebih lambat sehingga kasus kesalahan langka bisa mendapatkan pengecualian.

Yang membawa titik yang baik meskipun: C # setidaknya terdefinisi dengan baik sebagai konsekuensi dari kesalahan jenis. Ketik kesalahan saat runtime menghasilkan perilaku waras dalam bentuk pengecualian. Ini tidak seperti C atau C + + di mana compiler dapat dan akan dengan senang hati menghasilkan kode yang melakukan hal-hal gila yang sewenang-wenang.

Ada beberapa cara lain di mana sistem C # tidak sehat menurut desain.

  • Sistem tipe C # tidak (belum!) Melacak nol jenis referensi. Jika Anda mempertimbangkan mendapatkan pengecualian referensi null menjadi semacam kesalahan tipe runtime, maka C # sangat tidak sehat karena hampir tidak ada yang mencegah kesalahan semacam ini. Kemungkinan akan ada pekerjaan yang dilakukan untuk mengatasi hal ini di C # 8, tetapi itu tidak akan terdengar.

  • Banyak ekspresi cast memungkinkan pengguna untuk mengganti sistem jenis dan menyatakan "Saya tahu ekspresi ini akan menjadi tipe yang lebih spesifik pada saat runtime, dan jika saya salah, melempar pengecualian". (Beberapa pemeran berarti sebaliknya: "Saya tahu ekspresi ini adalah tipe X, tolong buat kode untuk mengonversinya ke nilai setara tipe Y". Biasanya aman.) Karena ini adalah tempat di mana pengembang secara khusus mengatakan bahwa mereka tahu lebih baik daripada sistem tipe, orang tidak bisa menyalahkan sistem jenis untuk kecelakaan yang dihasilkan.

Ada juga beberapa fitur yang menghasilkan perilaku seperti pemain meskipun tidak ada pemain dalam kode. Misalnya, jika Anda memiliki daftar hewan yang bisa Anda katakan

foreach(Giraffe g in animals)

dan jika ada harimau di sana, program Anda akan macet. Seperti catatan spesifikasi, kompilator hanya menyisipkan pemain atas nama Anda. (Jika Anda ingin melewati semua jerapah dan mengabaikan harimau, itu foreach(Giraffe g in animals.OfType<Giraffe>()).)

  • Itu unsafe subset dari C # membuat semua taruhan dibatalkan; Anda dapat melanggar aturan runtime sewenang-wenang dengannya. Mematikan sistem keamanan mematikan sistem keamanan, jadi tidak mengherankan bahwa C # tidak berbunyi ketika Anda mematikan pemeriksaan kesehatan.

15
2018-05-31 08:36



Ini tidak terlalu sulit untuk membuat masalah yang tidak dapat dipecahkan oleh C # dalam jangka waktu yang wajar. Beberapa masalah yang ditimbulkannya (sering dikaitkan dengan inferensi generik / tipe) adalah masalah NP-hard. Eric Lippert menggambarkan satu contoh di sini:

class MainClass
{
    class T{}
    class F{}
    delegate void DT(T t);
    delegate void DF(F f);
    static void M(DT dt)
    {
        System.Console.WriteLine("true");
        dt(new T());
    }
    static void M(DF df)
    {
        System.Console.WriteLine("false");
        df(new F());
    }
    static T Or(T a1, T a2, T a3){return new T();}
    static T Or(T a1, T a2, F a3){return new T();}
    static T Or(T a1, F a2, T a3){return new T();}
    static T Or(T a1, F a2, F a3){return new T();}
    static T Or(F a1, T a2, T a3){return new T();}
    static T Or(F a1, T a2, F a3){return new T();}
    static T Or(F a1, F a2, T a3){return new T();}
    static F Or(F a1, F a2, F a3){return new F();}
    static T And(T a1, T a2){return new T();}
    static F And(T a1, F a2){return new F();}
    static F And(F a1, T a2){return new F();}
    static F And(F a1, F a2){return new F();}
    static F Not(T a){return new F();}
    static T Not(F a){return new T();}
    static void MustBeT(T t){}
    static void Main()
    {
        // Introduce enough variables and then encode any Boolean predicate:
        // eg, here we encode (!x3) & ((!x1) & ((x1 | x2 | x1) & (x2 | x3 | x2)))
        M(x1=>M(x2=>M(x3=>MustBeT(
          And(
            Not(x3), 
            And(
              Not(x1), 
              And(
                Or(x1, x2, x1), 
                Or(x2, x3, x2))))))));
    }
}

4
2018-05-29 17:25