Skip to content

Commit 6308e17

Browse files
tonerdoBillWagner
authored andcommitted
C# Versioning (#1197)
* add article outline * flesh out content for virtual modifier * flesh out content for override modifier * flesh out content for new modifier * add documentation metadata * complete second draft based on PR feedback * add versioning to toc * include code samples * fix broken SemVer link * address PR feedback * address PR feedback * address PR feedback * address PR feedback
1 parent 986be6a commit 6308e17

File tree

8 files changed

+283
-1
lines changed

8 files changed

+283
-1
lines changed

docs/csharp/versioning.md

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
---
2+
title: C# Versioning | C# Guide
3+
description: Understand how versioning works in C# and .NET
4+
keywords: .NET, .NET Core, C#
5+
author: tsolarin
6+
manager: wpickett
7+
ms.date: 01/08/2017
8+
ms.topic: article
9+
ms.prod: visual-studio-dev-14
10+
ms.technology: devlang-csharp
11+
ms.devlang: csharp
12+
ms.assetid: aa8732d7-5cd0-46e1-994a-78017f20d861
13+
---
14+
15+
# Versioning in C# #
16+
17+
In this tutorial you'll learn what versioning means in .NET. You'll also learn the factors to consider when versioning your library as well as upgrading
18+
to a new version of the a library.
19+
20+
## Authoring Libraries
21+
22+
As a developer who has created .NET libraries for public use, you've most likely been in situations where you have
23+
to roll out new updates. How you go about this process matters a lot as you need to ensure that there's a seamless transition
24+
of existing code to the new version of your library. Here are several things to consider when creating a new release:
25+
26+
### Semantic Versioning
27+
28+
[Semantic versioning](http://semver.org/) (SemVer for short) is a naming convention applied to versions of your library to signify specific milestone events.
29+
Ideally, the version information you give your library should help developers determine the compatibility
30+
with their projects that make use of older versions of that same library.
31+
32+
The most basic approach to SemVer is the 3 component format `MAJOR.MINOR.PATCH`, where:
33+
34+
* `MAJOR` is incremented when you make incompatible API changes
35+
* `MINOR` is incremented when you add functionality in a backwards-compatible manner
36+
* `PATCH` is incremented when you make backwards-compatible bug fixes
37+
38+
There are also ways to specify other scenarios like pre-release versions etc. when applying version information to your .NET library.
39+
40+
### Backwards Compatibility
41+
42+
As you release new versions of your library, backwards compatibility with previous versions will most likely be one of your major concerns.
43+
A new version of your library is source compatible with a previous version if code that depends on the previous version can, when recompiled, work with the new version.
44+
A new version of your library is binary compatible if an application that depended on the old version can, without recompilation, work with the new version.
45+
46+
Here are some things to consider when trying to maintain backwards compatibility with older versions of your library:
47+
48+
* Virtual methods: When you make a virtual method non-virtual in your new version it means that projects that override that method
49+
will have to be updated. This is a huge breaking change and is strongly discouraged.
50+
* Method signatures: When updating a method behaviour requires you to change its signature as well, you should instead create an overload so that code calling into that method will still work.
51+
You can always manipulate the old method signature to call into the new method signature so that implementation remains consistent.
52+
* [Obsolete attribute](programming-guide/concepts/attributes/common-attributes.md#Obsolete): You can use this attribute in your code to specify classes or class members that are deprecated and likely to be removed in future versions.
53+
This ensures developers utilizing your library are better prepared for breaking changes.
54+
* Optional Method Arguments: When you make previously optional method arguments compulsory or change their default value then all code that does not supply those arguments will need to be updated.
55+
> [!NOTE]
56+
> Making compulsory arguments optional should have very little effect especially if it doesn't change the method's behaviour.
57+
58+
The easier you make it for your users to upgrade to the new version of your library, the more likely that they will upgrade sooner.
59+
60+
### Application Configuration File
61+
62+
As a .NET developer there's a very high chance you've encountered [the `app.config` file](https://msdn.microsoft.com/en-us/library/1fk1t1t0(v=vs.110).aspx) present in most project types.
63+
This simple configuration file can go a long way into improving the rollout of new updates. You should generally design your libraries in such
64+
a way that information that is likely to change regularly is stored in the `app.config` file, this way when such information is updated
65+
the config file of older versions just needs to be replaced with the new one without the need for recompilation of the library.
66+
67+
## Consuming Libraries
68+
69+
As a developer that consumes .NET libraries built by other developers you're most likely aware that a new version of a library might not be fully compatible with your project
70+
and you might often find yourself having to update your code to work with those changes.
71+
72+
Lucky for you C# and the .NET ecosystem comes with features and techniques that allow us to easily update our app to work with new versions of libraries that might introduce breaking changes.
73+
74+
### Assembly Binding Redirection
75+
76+
You can use the `app.config` file to update the version of a library your app uses. By adding what is called a [*binding redirect*](https://msdn.microsoft.com/en-us/library/7wd6ex19(v=vs.110).aspx) your
77+
can use the new library version without having to recompile your app. The following example shows how you would update
78+
your app's `app.config` file to use the `1.0.1` patch version of `ReferencedLibrary` instead of the `1.0.0` version it was originally compiled with.
79+
80+
```xml
81+
<dependentAssembly>
82+
<assemblyIdentity name="ReferencedLibrary" publicKeyToken="32ab4ba45e0a69a1" culture="en-us" />
83+
<bindingRedirect oldVersion="1.0.0" newVersion="1.0.1" />
84+
</dependentAssembly>
85+
```
86+
87+
> [!NOTE]
88+
> This approach will only work if the new version of `ReferencedLibrary` is binary compatible with your app.
89+
> See the [Backwards Compatibility](#backwards-compatibility) section above for changes to look out for when determining compatibility.
90+
91+
### new
92+
93+
You use the `new` modifier to hide inherited members of a base class. This is one way derived classes can respond to updates in base classes.
94+
95+
Take the following example:
96+
97+
[!code-csharp[Sample usage of the 'new' modifier](../../samples/csharp/versioning/new/Program.cs#sample)]
98+
99+
**Output**
100+
101+
```
102+
A base method
103+
A derived method
104+
```
105+
106+
From the example above you can see how `DerivedClass` hides the `MyMethod` method present in `BaseClass`.
107+
This means that when a base class in the new version of a library adds a member that already exists in your derived class, you can
108+
simply use the `new` modifier on your derived class member to hide the base class member.
109+
110+
When no `new` modifier is specified, a derived class will by default hide conflicting members in a base class,
111+
although a compiler warning will be generated the code will still compile. This means that simply adding new members to an existing class
112+
makes that new version of your library both source and binary compatible with code that depends on it.
113+
114+
### override
115+
116+
The `override` modifier means a derived implementation extends the implementation of a base class member rather than
117+
hides it. The base class member needs to have the `virtual` modifier applied to it.
118+
119+
[!code-csharp[Sample usage of the 'override' modifier](../../samples/csharp/versioning/override/Program.cs#sample)]
120+
121+
**Output**
122+
123+
```
124+
Base Method One: Method One
125+
Derived Method One: Derived Method One
126+
```
127+
128+
The `override` modifier is evaluated at compile time and the compiler will throw an error if it doesn't find a virtual member to override.
129+
130+
Your knowledge of the discussed techniques as well as your understanding of what situations to use them will go a long way to boost the ease
131+
of transition between versions of a library.

docs/toc.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,8 @@
311311
#### [Summary](csharp/expression-trees-summary.md)
312312
### [Native interoperability](csharp/programming-guide/interop/interoperability.md)
313313
### [🔧 Reflection & code generation](csharp/reflection.md)
314-
### [Documenting your code](csharp/codedoc.md)
314+
### [Documenting your code](csharp/codedoc.md)
315+
### [Versioning](csharp/versioning.md)
315316
## [🔧 Using the .NET Compiler Platform](csharp/roslyn/index.md)
316317

317318
## [C# Programming Guide](csharp/programming-guide/index.md)
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using System;
2+
3+
class Program
4+
{
5+
#region sample
6+
public class BaseClass
7+
{
8+
public void MyMethod()
9+
{
10+
Console.WriteLine("A base method");
11+
}
12+
}
13+
14+
public class DerivedClass : BaseClass
15+
{
16+
public new void MyMethod()
17+
{
18+
Console.WriteLine("A derived method");
19+
}
20+
}
21+
22+
public static void Main()
23+
{
24+
BaseClass b = new BaseClass();
25+
DerivedClass d = new DerivedClass();
26+
27+
b.MyMethod();
28+
d.MyMethod();
29+
}
30+
#endregion
31+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
C# Versioning Sample
2+
================
3+
4+
This sample is created during the [Versioning](https://docs.microsoft.com/dotnet/articles/csharp/versioning)
5+
for learning C# features. Please see that topic for detailed steps on the code
6+
for this sample.
7+
8+
Key Features
9+
------------
10+
11+
This sample contains code demonstrating the use of
12+
the `new` modifier when versioning .NET libraries
13+
14+
Build and Run
15+
-------------
16+
17+
To build and run the sample, type the following three commands:
18+
19+
`dotnet restore`
20+
`dotnet build`
21+
`dotnet run`
22+
23+
`dotnet restore` installs all the dependencies for this sample into the current directory.
24+
`dotnet build` creates the output assembly (or assemblies).
25+
`dotnet run` runs the output assembly.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"version": "1.0.0-*",
3+
"buildOptions": {
4+
"debugType": "portable",
5+
"emitEntryPoint": true
6+
},
7+
"dependencies": {},
8+
"frameworks": {
9+
"netcoreapp1.0": {
10+
"dependencies": {
11+
"Microsoft.NETCore.App": {
12+
"type": "platform",
13+
"version": "1.0.0"
14+
}
15+
},
16+
"imports": "dnxcore50"
17+
}
18+
}
19+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using System;
2+
3+
class Program
4+
{
5+
#region sample
6+
public class MyBaseClass
7+
{
8+
public virtual string MethodOne()
9+
{
10+
return "Method One";
11+
}
12+
}
13+
14+
public class MyDerivedClass : MyBaseClass
15+
{
16+
public override string MethodOne()
17+
{
18+
return "Derived Method One";
19+
}
20+
}
21+
22+
public static void Main()
23+
{
24+
MyBaseClass b = new MyBaseClass();
25+
MyDerivedClass d = new MyDerivedClass();
26+
27+
Console.WriteLine("Base Method One: {0}", b.MethodOne());
28+
Console.WriteLine("Derived Method One: {0}", d.MethodOne());
29+
}
30+
#endregion
31+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
C# Versioning Sample
2+
================
3+
4+
This sample is created during the [Versioning](https://docs.microsoft.com/dotnet/articles/csharp/versioning)
5+
for learning C# features. Please see that topic for detailed steps on the code
6+
for this sample.
7+
8+
Key Features
9+
------------
10+
11+
This sample contains code demonstrating the use of
12+
the `override` modifier when versioning .NET libraries
13+
14+
Build and Run
15+
-------------
16+
17+
To build and run the sample, type the following three commands:
18+
19+
`dotnet restore`
20+
`dotnet build`
21+
`dotnet run`
22+
23+
`dotnet restore` installs all the dependencies for this sample into the current directory.
24+
`dotnet build` creates the output assembly (or assemblies).
25+
`dotnet run` runs the output assembly.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"version": "1.0.0-*",
3+
"buildOptions": {
4+
"debugType": "portable",
5+
"emitEntryPoint": true
6+
},
7+
"dependencies": {},
8+
"frameworks": {
9+
"netcoreapp1.0": {
10+
"dependencies": {
11+
"Microsoft.NETCore.App": {
12+
"type": "platform",
13+
"version": "1.0.0"
14+
}
15+
},
16+
"imports": "dnxcore50"
17+
}
18+
}
19+
}

0 commit comments

Comments
 (0)