Tôi muốn thu thập càng nhiều thông tin càng tốt về phiên bản API trong .NET / CLR và cụ thể cách thay đổi API thực hiện hoặc không phá vỡ các ứng dụng khách. Đầu tiên, hãy xác định một số thuật ngữ:
Thay đổi API - một thay đổi trong định nghĩa hiển thị công khai của một loại, bao gồm bất kỳ thành viên nào. Điều này bao gồm thay đổi tên thành viên và loại thành viên, thay đổi loại cơ sở của loại, thêm / xóa giao diện khỏi danh sách các giao diện đã triển khai của loại, thêm / xóa thành viên (bao gồm quá tải), thay đổi mức độ hiển thị của thành viên, đổi tên phương thức và tham số loại, thêm giá trị mặc định cho các tham số phương thức, thêm / xóa thuộc tính trên các loại và thành viên và thêm / xóa tham số loại chung trên các loại và thành viên (tôi có bỏ sót điều gì không?). Điều này không bao gồm bất kỳ thay đổi nào trong các cơ quan thành viên hoặc bất kỳ thay đổi nào đối với các thành viên tư nhân (nghĩa là chúng tôi không tính đến Reflection).
Ngắt cấp nhị phân - một thay đổi API dẫn đến các cụm máy khách được biên dịch theo phiên bản API cũ hơn có khả năng không tải với phiên bản mới. Ví dụ: thay đổi chữ ký phương thức, ngay cả khi nó cho phép được gọi theo cùng một cách như trước đây (nghĩa là: void để trả về giá trị mặc định của loại / tham số mặc định).
Ngắt cấp độ nguồn - một thay đổi API dẫn đến mã hiện tại được viết để biên dịch theo phiên bản cũ hơn của API có khả năng không biên dịch với phiên bản mới. Tuy nhiên, đã biên dịch các cụm máy khách hoạt động như trước đây. Ví dụ: thêm một quá tải mới có thể dẫn đến sự mơ hồ trong các cuộc gọi phương thức không rõ ràng trước đó.
Thay đổi ngữ nghĩa yên tĩnh ở mức nguồn - một thay đổi API dẫn đến mã hiện tại được viết để biên dịch theo phiên bản cũ hơn của API lặng lẽ thay đổi ngữ nghĩa của nó, ví dụ bằng cách gọi một phương thức khác. Tuy nhiên, mã nên tiếp tục biên dịch mà không có cảnh báo / lỗi và các hội đồng được biên dịch trước đó sẽ hoạt động như trước. Ví dụ: triển khai giao diện mới trên một lớp hiện có dẫn đến tình trạng quá tải khác nhau được chọn trong quá trình phân giải quá tải.
Mục tiêu cuối cùng là lập danh mục càng nhiều thay đổi API ngữ nghĩa phá vỡ và yên tĩnh càng tốt và mô tả chính xác hiệu quả của sự phá vỡ, và ngôn ngữ nào và không bị ảnh hưởng bởi nó. Để mở rộng về sau: trong khi một số thay đổi ảnh hưởng đến tất cả các ngôn ngữ trên toàn cầu (ví dụ: thêm thành viên mới vào giao diện sẽ phá vỡ việc triển khai giao diện đó bằng bất kỳ ngôn ngữ nào), một số yêu cầu ngữ nghĩa ngôn ngữ rất cụ thể để tham gia để nghỉ ngơi. Điều này điển hình nhất liên quan đến quá tải phương thức và, nói chung, bất cứ điều gì phải làm với chuyển đổi kiểu ngầm định. Dường như không có cách nào để định nghĩa "mẫu số chung nhỏ nhất" ở đây ngay cả đối với các ngôn ngữ tuân thủ CLS (nghĩa là các ngôn ngữ tuân thủ ít nhất theo quy tắc của "người tiêu dùng CLS" như được định nghĩa trong thông số CLI) - mặc dù tôi ' sẽ đánh giá cao nếu ai đó sửa tôi là sai ở đây - vì vậy điều này sẽ phải đi theo ngôn ngữ. Những người quan tâm nhất đương nhiên là những người đi kèm với .NET: C #, VB và F #; nhưng những thứ khác, chẳng hạn như IronPython, IronRuby, Delphi Prism v.v ... cũng có liên quan. Càng nhiều trường hợp góc, nó sẽ càng thú vị hơn - những thứ như loại bỏ các thành viên khá rõ ràng, nhưng các tương tác tinh tế giữa quá tải phương thức, tham số tùy chọn / mặc định, suy luận kiểu lambda và toán tử chuyển đổi có thể rất đáng ngạc nhiên nhiều lúc
Một vài ví dụ để khởi động điều này:
Thêm quá tải phương thức mới
Loại: phá vỡ mức nguồn
Ngôn ngữ bị ảnh hưởng: C #, VB, F #
API trước khi thay đổi:
public class Foo
{
public void Bar(IEnumerable x);
}
API sau khi thay đổi:
public class Foo
{
public void Bar(IEnumerable x);
public void Bar(ICloneable x);
}
Mã khách hàng mẫu làm việc trước khi thay đổi và bị hỏng sau đó:
new Foo().Bar(new int[0]);
Thêm quá tải toán tử chuyển đổi ngầm định mới
Loại: phá vỡ cấp nguồn.
Ngôn ngữ bị ảnh hưởng: C #, VB
Ngôn ngữ không bị ảnh hưởng: F #
API trước khi thay đổi:
public class Foo
{
public static implicit operator int ();
}
API sau khi thay đổi:
public class Foo
{
public static implicit operator int ();
public static implicit operator float ();
}
Mã khách hàng mẫu làm việc trước khi thay đổi và bị hỏng sau đó:
void Bar(int x);
void Bar(float x);
Bar(new Foo());
Lưu ý: F # không bị hỏng, vì nó không có bất kỳ mức hỗ trợ ngôn ngữ nào cho các toán tử bị quá tải, không rõ ràng cũng không ẩn - cả hai phải được gọi trực tiếp là op_Explicit
vàop_Implicit
các phương thức.
Thêm phương thức mới
Loại: thay đổi ngữ nghĩa yên tĩnh cấp nguồn.
Ngôn ngữ bị ảnh hưởng: C #, VB
Ngôn ngữ không bị ảnh hưởng: F #
API trước khi thay đổi:
public class Foo
{
}
API sau khi thay đổi:
public class Foo
{
public void Bar();
}
Mã khách hàng mẫu chịu một thay đổi ngữ nghĩa yên tĩnh:
public static class FooExtensions
{
public void Bar(this Foo foo);
}
new Foo().Bar();
Lưu ý: F # không bị hỏng, vì nó không hỗ trợ mức ngôn ngữ ExtensionMethodAttribute
và yêu cầu các phương thức mở rộng CLS được gọi là phương thức tĩnh.