For example a Person
that has only one Age
method:
public class Person
{
public int Age()
{
return 6;
}
}
A Height()
method is added.
public class Person
{
public int Age()
{
return 6;
}
public int Height()
{
return 6;
}
}
Is that a breaking change? Pay attention that Person
is not sealed.
tl;dr;
Eric Lippert is right, again..
Thanks to Tim Schmelter's comment with the link to Eric Lippert's blog post, I've changed my original answer.
Adding a public method or property to an existing class is potentially a breaking change. Probably not, but it might be!
The longer version
A breaking change means you are changing the public API of your type in such a way that existing code working with the current API will no longer be able to compile.
Obvious breaking changes are removing public members, or altering them in such a way that the code using your class can't be compiled anymore.
Such changes might be (a partial list!):
- adding a non-optional argument to an existing method.*
- changing the data type of an argument of an existing method to a data type that's not a base type of the original argument
- changing the data type of a property to a data type that is not deriving from the original data type
- changing the return type of a method to a type that's not derived from the original type
As Eric demonstrates, even adding a new public method is potentially a breaking change, and just like in the breaking overload he blogged about, so does changing types of properties or arguments to a more specific types (meaning derived from original types) might also be a breaking change, for the exact same reasons.
However, Eric also points out that such scenarios are so specific and unlikely, that it's probably safe to assume you will not encounter them.
This particular "flavour" of breaking change is an odd one in that it makes almost every possible change to the surface area of a type into a potential breaking change, while at the same time being such an obviously contrived and unlikely scenario that no "real world" developers are likely to run into it.
*While we are on the subject of breaking changes, changing an optional argument default value is also a potential breaking change - since optional arguments works by injecting the default value to the calling method as an argument passed to the method that have an optional argument. Changing the default value means you have to recompile everything that uses the method.
It will be a breaking change if any derived class already declares a Hight
property and has "treat warnings as errors" turned on.
The change will produce a CS0108 warning (which becomes an error) unless or until the owner of this class adds the new
keyword.
Thanks for the links Zohar and Tim; Zohar's answer is correct. In case it is not completely clear what the breaking scenario is, consider:
public sealed class Person
{
public TimeSpan Age { get; set; }
}
public sealed class Building
{
public double Height { get; set; }
}
public sealed class Weird
{
public static void M(Func<Person, double> f) {}
public static void M(Func<Building, double> f) {}
public static void N() {
M(x => x.Height);
}
}
This program compiles without error because overload resolution correctly deduces that x
must be Building
. But if we add property public double Height {get; set;}
to Person
, then Weird.N
contains an ambiguity error because we cannot know which is intended to be the type of x
.
In practice, this sort of breaking change is rare enough that we explicitly decided to not consider it important when designing language features and libraries.
To add possible breaking changes to the list, here is a rather nasty one:
Company A publishes its grandiose new class Foo
. Company B loves the new stuff but misses some functionality that it decides to implement via en extension method tailored exactly to their needs:
namespace A {
public class Foo { ... } }
namespace B {
static class Extensions {
public static string Frob(this Foo foo) { .... } } }
Company A realizes that it could do better and decides to upgrade Foo
with some new functionality and decides to add a new method string Frob()
with a general purpose implementation.
Company A publishes the updated library and Company B happily updates its software with the new version. Everything compiles fine... but a breaking change happened just the same; foo.Frob()
is now resolved to Foo.Frob()
and not to the extension method Extensions.Frob(this Foo foo)
.