This page shows the source for this entry, with WebCore formatting language tags and attributes highlighted.
Title
Breaking Changes in C#
Description
Due to the nature of the language, there are some API changes that almost inevitably lead to breaking changes in C#.
<h>Change constructor parameters</h>
While you can easily make another constructor, marking the old one(s) as obsolete, if you use an IOC that allows only a single public constructor, you're forced to either
<ul>
remove the obsolete constructor or
mark the obsolete constructor as <c>protected</c>.
</ul>
In either case, the user has a compile error.
<h>Virtual methods/Interfaces</h>
There are several known issues with introducing new methods or changing existing methods on an existing interface. For many of these situations, there are relatively smooth upgrade paths.
I encountered a situation recently that I thought worth mentioning. I wanted to introduce a new <i>overload</i> on an existing type.
Suppose you have the following method:
<code>bool TryGetValue<t>(
out T value,
TKey key = default(TKey),
[CanBeNull] ILogger logger = null
);</code>
We would like to remove the <c>logger</c> parameter. So we deprecate the method above and declare the new method.
<code>bool TryGetValue<t>(
out T value,
TKey key = default(TKey)
);</code>
Now the compiler/ReSharper notifies you that there will be an ambiguity if a caller does not pass a <c>logger</c>. How to resolve this? Well, we can just remove the default value for that parameter in the obsolete method.
<code>bool TryGetValue<t>(
out T value,
TKey key = default(TKey),
[CanBeNull] ILogger logger
);</code>
But now you've got another problem: The parameter <c>logger</c> cannot come after the <c>key</c> parameter because it doesn't have a default value.
So, now you'd have to <i>move</i> the <c>logger</c> parameter in front of the key parameter. This will cause a compile error in clients, which is what we were trying to avoid in the first place.
In this case, we have a couple of sub-optimal options.
<dl dt_class="field">
Multiple Releases
<div>Use a different name for the new API (e.g. <c>TryGetValueEx</c> à la Windows) in the next major version, then switch the name back in the version <i>after that</i> and finally remove the obsolete member in <i>yet another</i> version.
That is,
<ul>
in version <i>n</i>, <c>TryGetValue</c> (with logger) is obsolete and users are told to use <c>TryGetValueEx</c> (no logger)
in version <i>n+1</i>, <c>TryGetValueEx</c> (no logger) is obsolete and users are told to use <c>TryGetValue</c> (no logger)
in version <i>n+2</i>, we finally remove <c>TryGetValueEx</c>.
</ul>
This is a lot of work and requires three upgrades to accomplish. You really need to stay on the ball in order to get this kind of change integrated and it takes a non-trivial amount of time and effort.
We generally don't use this method, as our customers are developers and can deal with a compile error or two, especially when it's noted in the release notes <i>and</i> the workaround is fairly obvious (e.g. the <c>logger</c> parameter is just no longer required).</div>
Remove instead of deprecating
Accept that there will be a compile error and soften the landing as much as possible for customers by noting it in the release notes.
</dl>