.NET / CLR'de API sürümlendirmesi ve özellikle API değişikliklerinin istemci uygulamalarını nasıl kırdığı veya bozmadığı hakkında mümkün olduğunca fazla bilgi toplamak istiyorum. İlk olarak, bazı terimleri tanımlayalım:
API değişikliği - herkese açık üyeleri de dahil olmak üzere bir türün herkes tarafından görülebilir tanımında yapılan bir değişiklik. Bu, tür ve üye adlarını değiştirme, bir türün temel türünü değiştirme, bir türün uygulanmış arabirimler listesine arabirim ekleme / kaldırma, üye ekleme / kaldırma (aşırı yükler dahil), üye görünürlüğünü değiştirme, yeniden adlandırma yöntemi ve tür parametreleri, varsayılan değerleri ekleme içerir yöntem parametreleri, türler ve üyeler üzerinde öznitelik ekleme / kaldırma ve türler ve üyeler üzerinde genel tür parametreleri ekleme / kaldırma (bir şey kaçırdım mı?). Bu, üye organlarındaki herhangi bir değişikliği veya özel üyelerde yapılan değişiklikleri içermez (yani, Yansımayı dikkate almayız).
İkili düzey sonu - potansiyel olarak yeni sürümle yüklenmeyen API'nin eski sürümüne karşı derlenen istemci derlemelerine neden olan bir API değişikliği. Örnek: önceki gibi aynı şekilde çağrılmasına izin verse bile yöntem imzasını değiştirme (yani: tür / parametre varsayılan değerlerinin aşırı yüklenmesi için geçersiz).
Kaynak düzeyinde kesme - potansiyel olarak yeni sürümle derlenmeyen API'nin eski sürümüne karşı derlenmek üzere yazılmış kodla sonuçlanan bir API değişikliği. Zaten derlenmiş istemci derlemeleri daha önce olduğu gibi çalışır. Örnek: daha önce kesin olmayan yöntem çağrılarında belirsizliğe yol açabilecek yeni bir aşırı yük eklemek.
Kaynak düzeyinde sessiz semantik değişikliği - mevcut kodun API'nin eski sürümüne göre derlenmesi için yazılan bir API değişikliği, örneğin farklı bir yöntem çağırarak anlambilimini sessizce değiştirir. Ancak kod hiçbir uyarı / hata olmadan derlemeye devam etmeli ve daha önce derlenmiş derlemeler önceki gibi çalışmalıdır. Örnek: Aşırı yük çözünürlüğü sırasında farklı bir aşırı yükün seçilmesine neden olan mevcut bir sınıfa yeni bir arayüz uygulamak.
Nihai amaç, mümkün olduğunca çok sayıda kırılma ve sessiz anlambilim API değişikliklerini kataloglamak ve kırmanın kesin etkisini ve hangi dillerin bundan etkilendiğini ve etkilenmediğini tanımlamaktır. İkincisini genişletmek için: bazı değişiklikler evrensel olarak tüm dilleri etkilerken (örn. Bir arayüze yeni bir üye eklemek, herhangi bir dilde bu arayüzün uygulamalarını kıracaktır), bazıları bir mola için oyuna girmek için çok özel bir dil semantiği gerektirir. Bu tipik olarak yöntem aşırı yüklemesini ve genel olarak örtük tip dönüşümlerle ilgili olan her şeyi içerir. Burada "en az ortak payda" yı CLS uyumlu diller için bile tanımlamanın bir yolu yok gibi görünüyor (yani en azından CLI spesifikasyonunda tanımlandığı gibi "CLS tüketici" kurallarına uyanlar) - Birisi beni burada yanlış olarak düzeltirse sevinirim - bu yüzden dile göre dil gitmek zorunda kalacak. En çok ilgilenenler doğal olarak .NET ile birlikte gelenlerdir: C #, VB ve F #; ancak IronPython, IronRuby, Delphi Prism vb. diğerleri de geçerlidir. Bir köşe vakası ne kadar fazlaysa, o kadar ilginç olur - üyeleri kaldırmak gibi şeyler oldukça belirgindir, ancak yöntem aşırı yüklenmesi, isteğe bağlı / varsayılan parametreler, lambda tipi çıkarım ve dönüşüm operatörleri arasındaki ince etkileşimler çok şaşırtıcı olabilir zaman zaman.
Bunu başlatmak için birkaç örnek:
Yeni yöntem aşırı yüklemeleri ekleme
Tür: kaynak düzeyinde mola
Etkilenen diller: C #, VB, F #
Değişiklikten önce API:
public class Foo
{
public void Bar(IEnumerable x);
}
Değişiklikten sonra API:
public class Foo
{
public void Bar(IEnumerable x);
public void Bar(ICloneable x);
}
Değişiklikten önce çalışan ve bundan sonra kırılan örnek istemci kodu:
new Foo().Bar(new int[0]);
Yeni örtülü dönüşüm operatörü aşırı yüklemeleri ekleme
Tür: kaynak düzeyinde mola.
Etkilenen diller: C #, VB
Etkilenmeyen diller: F #
Değişiklikten önce API:
public class Foo
{
public static implicit operator int ();
}
Değişiklikten sonra API:
public class Foo
{
public static implicit operator int ();
public static implicit operator float ();
}
Değişiklikten önce çalışan ve bundan sonra kırılan örnek istemci kodu:
void Bar(int x);
void Bar(float x);
Bar(new Foo());
Notlar: F # bozuk değildir, çünkü aşırı yüklenmiş işleçler için herhangi bir dil düzeyi desteği yoktur, ne açık ne de örtük - her ikisi de doğrudan olarak op_Explicit
ve op_Implicit
yöntemler olarak adlandırılmalıdır .
Yeni örnek yöntemleri ekleme
Tür: kaynak düzeyinde sessiz semantik değişim.
Etkilenen diller: C #, VB
Etkilenmeyen diller: F #
Değişiklikten önce API:
public class Foo
{
}
Değişiklikten sonra API:
public class Foo
{
public void Bar();
}
Sessiz bir anlam değişikliği içeren örnek istemci kodu:
public static class FooExtensions
{
public void Bar(this Foo foo);
}
new Foo().Bar();
Notlar: F # bozuk değildir, çünkü dil seviyesi desteği yoktur ExtensionMethodAttribute
ve CLS genişletme yöntemlerinin statik yöntem olarak adlandırılmasını gerektirir.