Training
Module
Call methods from the .NET Class Library using C# - Training
Use functionality in the .NET Class Library by calling methods that return values, accept input parameters, and more.
This browser is no longer supported.
Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.
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 to a new version of a library.
The C# compiler is part of the .NET SDK. By default, the compiler chooses the C# language version that matches the chosen TFM for your project. If the SDK version is greater than your chosen framework, the compiler could use a greater language version. You can change the default by setting the LangVersion
element in your project. You can learn how in our article on compiler options.
Warning
Setting the LangVersion
element to latest
is discouraged. The latest
setting means the installed compiler uses its latest version. That can change from machine to machine, making builds unreliable. In addition, it enables language features that may require runtime or library features not included in the current SDK.
As a developer who has created .NET libraries for public use, you've most likely been in situations where you have 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 of existing code to the new version of your library. Here are several things to consider when creating a new release:
Semantic versioning (SemVer for short) is a naming convention applied to versions of your library to signify specific milestone events. Ideally, the version information you give your library should help developers determine the compatibility with their projects that make use of older versions of that same library.
The most basic approach to SemVer is the 3 component format MAJOR.MINOR.PATCH
, where:
MAJOR
is incremented when you make incompatible API changesMINOR
is incremented when you add functionality in a backwards-compatible mannerPATCH
is incremented when you make backwards-compatible bug fixesThere are also ways to specify other scenarios, for example, pre-release versions, when applying version information to your .NET library.
As you release new versions of your library, backwards compatibility with previous versions will most likely be one of your major concerns. 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. 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.
Here are some things to consider when trying to maintain backwards compatibility with older versions of your library:
Note
Making compulsory arguments optional should have very little effect especially if it doesn't change the method's behavior.
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.
As a .NET developer there's a very high chance you've encountered the app.config
file present in most project types.
This simple configuration file can go a long way into improving the rollout of new updates. You should generally design your libraries in such a way that information that is likely to change regularly is stored in the app.config
file, this way when such information is updated, the config file of older versions just needs to be replaced with the new one without the need for recompilation of the library.
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 and you might often find yourself having to update your code to work with those changes.
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.
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, you can use the new library version without having to recompile your app. The following example shows how you would update 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.
<dependentAssembly>
<assemblyIdentity name="ReferencedLibrary" publicKeyToken="32ab4ba45e0a69a1" culture="en-us" />
<bindingRedirect oldVersion="1.0.0" newVersion="1.0.1" />
</dependentAssembly>
Note
This approach will only work if the new version of ReferencedLibrary
is binary compatible with your app.
See the Backwards Compatibility section above for changes to look out for when determining compatibility.
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.
Take the following example:
public class BaseClass
{
public void MyMethod()
{
Console.WriteLine("A base method");
}
}
public class DerivedClass : BaseClass
{
public new void MyMethod()
{
Console.WriteLine("A derived method");
}
}
public static void Main()
{
BaseClass b = new BaseClass();
DerivedClass d = new DerivedClass();
b.MyMethod();
d.MyMethod();
}
Output
A base method
A derived method
From the example above you can see how DerivedClass
hides the MyMethod
method present in BaseClass
.
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 simply use the new
modifier on your derived class member to hide the base class member.
When no new
modifier is specified, a derived class will by default hide conflicting members in a base class, although a compiler warning will be generated the code will still compile. This means that simply adding new members to an existing class makes that new version of your library both source and binary compatible with code that depends on it.
The override
modifier means a derived implementation extends the implementation of a base class member rather than
hides it. The base class member needs to have the virtual
modifier applied to it.
public class MyBaseClass
{
public virtual string MethodOne()
{
return "Method One";
}
}
public class MyDerivedClass : MyBaseClass
{
public override string MethodOne()
{
return "Derived Method One";
}
}
public static void Main()
{
MyBaseClass b = new MyBaseClass();
MyDerivedClass d = new MyDerivedClass();
Console.WriteLine("Base Method One: {0}", b.MethodOne());
Console.WriteLine("Derived Method One: {0}", d.MethodOne());
}
Output
Base Method One: Method One
Derived Method One: Derived Method One
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.
Your knowledge of the discussed techniques and your understanding of the situations in which to use them, will go a long way towards easing the transition between versions of a library.
.NET feedback
.NET is an open source project. Select a link to provide feedback:
Training
Module
Call methods from the .NET Class Library using C# - Training
Use functionality in the .NET Class Library by calling methods that return values, accept input parameters, and more.