This page shows the source for this entry, with WebCore formatting language tags and attributes highlighted.


Breaking Changes in C#


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>