Saya ingin mengumpulkan informasi sebanyak mungkin mengenai versi API di .NET / CLR, dan secara khusus bagaimana perubahan API dilakukan atau tidak merusak aplikasi klien. Pertama, mari kita definisikan beberapa istilah:
Perubahan API - perubahan dalam definisi jenis yang terlihat secara publik, termasuk anggota publiknya. Ini termasuk mengubah tipe dan nama anggota, mengubah tipe dasar suatu tipe, menambah / menghapus antarmuka dari daftar antarmuka tipe yang diimplementasikan, menambah / menghapus anggota (termasuk kelebihan beban), mengubah visibilitas anggota, metode penamaan nama dan parameter tipe, menambahkan nilai default untuk parameter metode, menambah / menghapus atribut pada tipe dan anggota, dan menambahkan / menghapus parameter tipe umum pada tipe dan anggota (apakah saya melewatkan sesuatu?). Ini tidak termasuk perubahan apa pun dalam tubuh anggota, atau perubahan apa pun pada anggota pribadi (yaitu kami tidak mempertimbangkan Refleksi akun).
Istirahat tingkat biner - perubahan API yang menghasilkan kumpulan klien yang dikompilasi terhadap versi API yang lebih lama yang berpotensi tidak dimuat dengan versi baru. Contoh: mengubah metode tanda tangan, bahkan jika memungkinkan untuk dipanggil dengan cara yang sama seperti sebelumnya (yaitu: batal untuk mengembalikan tipe / parameter nilai default kelebihan beban).
Istirahat tingkat sumber - perubahan API yang menghasilkan kode yang ada ditulis untuk dikompilasi terhadap versi API yang lebih lama yang berpotensi tidak dikompilasi dengan versi yang baru. Namun, kumpulan klien yang telah dikompilasi berfungsi seperti sebelumnya. Contoh: menambahkan kelebihan baru yang dapat mengakibatkan ambiguitas dalam panggilan metode yang tidak ambigu sebelumnya.
Semantik diam tingkat sumber berubah - perubahan API yang menghasilkan kode yang ada ditulis untuk mengkompilasi terhadap versi lama dari API diam-diam mengubah semantiknya, misalnya dengan memanggil metode yang berbeda. Namun kode harus terus dikompilasi tanpa peringatan / kesalahan, dan majelis yang dikompilasi sebelumnya harus berfungsi seperti sebelumnya. Contoh: mengimplementasikan antarmuka baru pada kelas yang ada yang menghasilkan kelebihan beban yang berbeda yang dipilih selama resolusi kelebihan beban.
Tujuan utamanya adalah untuk mengkatalogkan sebanyak mungkin semantik dan semantik API yang berubah semaksimal mungkin, dan menjelaskan efek kerusakan yang tepat, dan bahasa mana yang dan tidak terpengaruh olehnya. Untuk memperluas yang terakhir: sementara beberapa perubahan memengaruhi semua bahasa secara universal (misalnya menambahkan anggota baru ke antarmuka akan memecah implementasi antarmuka itu dalam bahasa apa pun), beberapa memerlukan semantik bahasa yang sangat spesifik untuk ikut bermain untuk mendapatkan jeda. Ini biasanya melibatkan kelebihan metode, dan, secara umum, apa pun yang berkaitan dengan konversi tipe implisit. Tampaknya tidak ada cara untuk mendefinisikan "penyebut paling umum" di sini bahkan untuk bahasa yang sesuai CLS (yaitu yang paling tidak sesuai dengan aturan "konsumen CLS" sebagaimana didefinisikan dalam spesifikasi CLI) - meskipun saya ' Saya akan menghargai jika ada yang mengoreksi saya sebagai orang yang salah di sini - jadi ini harus dilakukan berdasarkan bahasa. Yang paling menarik tentu saja yang datang dengan .NET di luar kotak: C #, VB dan F #; tetapi yang lain, seperti IronPython, IronRuby, Delphi Prism dll juga relevan. Semakin banyak sudut kasus, semakin menarik - hal-hal seperti menghapus anggota cukup jelas, tetapi interaksi yang halus antara misalnya kelebihan metode, parameter opsional / default, inferensi tipe lambda, dan operator konversi bisa sangat mengejutkan kadang.
Beberapa contoh untuk memulai ini:
Menambahkan kelebihan metode baru
Jenis: istirahat level sumber
Bahasa yang terpengaruh: C #, VB, F #
API sebelum perubahan:
public class Foo
{
public void Bar(IEnumerable x);
}
API setelah perubahan:
public class Foo
{
public void Bar(IEnumerable x);
public void Bar(ICloneable x);
}
Contoh kode klien yang berfungsi sebelum perubahan dan rusak setelahnya:
new Foo().Bar(new int[0]);
Menambahkan kelebihan operator konversi implisit baru
Jenis: istirahat level sumber.
Bahasa yang terpengaruh: C #, VB
Bahasa tidak terpengaruh: F #
API sebelum perubahan:
public class Foo
{
public static implicit operator int ();
}
API setelah perubahan:
public class Foo
{
public static implicit operator int ();
public static implicit operator float ();
}
Contoh kode klien yang berfungsi sebelum perubahan dan rusak setelahnya:
void Bar(int x);
void Bar(float x);
Bar(new Foo());
Catatan: F # tidak rusak, karena tidak memiliki dukungan tingkat bahasa untuk operator kelebihan beban, baik eksplisit maupun implisit - keduanya harus dipanggil langsung sebagai op_Explicit
dan op_Implicit
metode.
Menambahkan metode instance baru
Jenis: perubahan semantik sunyi tingkat sumber.
Bahasa yang terpengaruh: C #, VB
Bahasa tidak terpengaruh: F #
API sebelum perubahan:
public class Foo
{
}
API setelah perubahan:
public class Foo
{
public void Bar();
}
Contoh kode klien yang menderita perubahan semantik sunyi:
public static class FooExtensions
{
public void Bar(this Foo foo);
}
new Foo().Bar();
Catatan: F # tidak rusak, karena tidak memiliki dukungan tingkat bahasa untuk ExtensionMethodAttribute
, dan memerlukan metode ekstensi CLS disebut sebagai metode statis.