Мне пришлось ждать 4-5 месяцев. так что это выглядит довольно стандартно для такого длительного периода ожидания.
Тип: Разрыв на двоичном уровне
Затронутые языки: C # (VB и F #, скорее всего, но не проверено)
API до изменения
public static class Foo
{
public static void bar(int i);
}
API после изменения
public static class Foo
{
public static bool bar(int i);
}
Пример кода клиента, работающего до изменения
Foo.bar(13);
Вид: Разрыв уровня источника
Затрагиваемые языки: C #, VB
API до изменения:
public class Foo
{
public void Bar(string param);
}
API после изменения:
public class Foo
{
public void Bar(string param);
public void Bar(int? param);
}
Пример кода клиента, работающего до изменения и прерываемого после него:
new Foo().Bar(null);
Исключение: вызов неоднозначен между следующими методами или свойствами.
Когда я обнаружил, этот был очень неочевидным, особенно в свете разницы в той же ситуации для интерфейсов. Это совсем не перерыв, но достаточно удивительно, что я решил включить его:
Тип: не перерыв!
Затронутые языки: нет (т.е. ни один не сломан)
API до изменения:
class Foo
{
public virtual void Bar() {}
public virtual void Baz() {}
}
API после изменения:
class FooBase
{
public virtual void Bar() {}
}
class Foo : FooBase
{
public virtual void Baz() {}
}
Пример кода, который продолжает работать на протяжении всего изменения (хотя я ожидал, что он сломается):
// C++/CLI
ref class Derived : Foo
{
public virtual void Baz() {{
// Explicit override
public virtual void BarOverride() = Foo::Bar {}
};
Примечания:
C ++ / CLI - единственный. NET, который имеет конструкцию, аналогичную явной реализации интерфейса для членов виртуального базового класса - «явное переопределение». Я полностью ожидал, что это приведет к тому же типу поломки, что и при перемещении элементов интерфейса в базовый интерфейс (поскольку IL, сгенерированный для явного переопределения, такой же, как и для явной реализации). К моему удивлению, это не так - хотя сгенерированный IL по-прежнему указывает, что BarOverride
переопределяет Foo :: Bar
, а не FooBase :: Bar
, загрузчик сборки достаточно умен, чтобы правильно заменять один на другой без каких-либо претензий - очевидно, тот факт, что Foo
является классом, вот в чем разница. Иди разбери ...
Foo
является классом, вот что имеет значение. Иди разбери ... загрузчик сборок достаточно умен, чтобы правильно заменять один на другой без каких-либо нареканий - очевидно, тот факт, что Foo
является классом, вот что отличает. Поймите ... Это, возможно, не столь очевидный частный случай «добавления / удаления элементов интерфейса», и я решил, что он заслуживает собственная запись в свете другого случая, о котором я собираюсь опубликовать позже. Итак:
Тип: разрывы как на уровне исходного, так и на двоичном уровнях
Затронутые языки: C #, VB, C ++ / CLI, F # (для разрыва исходного кода; двоичный код естественно влияет на любой язык )
API до изменения:
interface IFoo
{
void Bar();
void Baz();
}
API после изменения:
interface IFooBase
{
void Bar();
}
interface IFoo : IFooBase
{
void Baz();
}
Пример кода клиента, который нарушается при изменении на уровне исходного кода:
class Foo : IFoo
{
void IFoo.Bar() { ... }
void IFoo.Baz() { ... }
}
Пример кода клиента, который нарушается при изменении на двоичном уровне;
(new Foo()).Bar();
Примечания:
Для разрыва исходного уровня проблема в том, что C #, VB и C ++ / CLI требуют точного имени интерфейса в объявлении реализации члена интерфейса; таким образом, если член перемещается в базовый интерфейс, код больше не будет компилироваться.
Двоичный разрыв происходит из-за того, что методы интерфейса полностью определены в сгенерированном IL для явных реализаций, и имя интерфейса также должно быть точным.
Неявная реализация, где она доступна (например, C # и C ++ / CLI, но не VB ) будет нормально работать как на исходном, так и на двоичном уровне. Вызов методов тоже не прерывается.
и имя интерфейса также должно быть точным.Неявная реализация, где она доступна (например, C # и C ++ / CLI, но не VB), будет нормально работать как на уровне исходного кода, так и на двоичном уровне. Вызов методов тоже не прерывается.
и имя интерфейса также должно быть точным.Неявная реализация, где она доступна (например, C # и C ++ / CLI, но не VB), будет нормально работать как на уровне исходного кода, так и на двоичном уровне. Вызов методов также не прерывается.
Изменение API:
Разрыв на двоичном уровне:
Добавление нового члена (защищенного от события), который использует тип из другой сборки (Class2) в качестве ограничения аргумента шаблона.
protected void Something (), где T: Class2 {}
Изменение дочернего класса (Class3) на производный от типа в другой сборке, когда класс используется в качестве аргумента шаблона для этого класса.
защищенный класс Class3: Class2 {}
protected void Something () где T: Class3 {}
Изменение тихой семантики на уровне источника:
(не уверен, где они подходят)
Изменения развертывания:
Bootstrap / Изменения конфигурации:
Обновление:
Извините, я не "
Вид нарушения: исходный код
Затронутые языки: все
Реорганизация явной реализации интерфейса в неявную осуществляется более тонко в том, как это может сломать API. На первый взгляд может показаться, что это должно быть относительно безопасно, однако в сочетании с наследованием это может вызвать проблемы.
API до изменения:
public class Foo : IEnumerable
{
IEnumerator IEnumerable.GetEnumerator() { yield return "Foo"; }
}
API после изменения:
public class Foo : IEnumerable
{
public IEnumerator GetEnumerator() { yield return "Foo"; }
}
Пример кода клиента, который работает до изменения и впоследствии не работает:
class Bar : Foo, IEnumerable
{
IEnumerator IEnumerable.GetEnumerator() // silently hides base instance
{ yield return "Bar"; }
}
foreach( var x in new Bar() )
Console.WriteLine(x); // originally output "Bar", now outputs "Foo"
Вид нарушения: исходный и двоичный
Затронутые языки: все
На самом деле это просто вариант изменения метода доступность - это немного тоньше, поскольку легко упустить из виду тот факт, что не весь доступ к методам интерфейса обязательно осуществляется через ссылку на тип интерфейса.
API до изменения:
public class Foo : IEnumerable
{
public IEnumerator GetEnumerator();
}
API после изменения:
public class Foo : IEnumerable
{
IEnumerator IEnumerable.GetEnumerator();
}
Пример кода клиента, который работает до изменения и впоследствии ломается:
new Foo().GetEnumerator(); // fails because GetEnumerator() is no longer public