Développer des bibliothèques avec l’interface CLI .NET
Cet article explique comment écrire des bibliothèques pour .NET à l’aide de l’interface CLI .NET. L’interface CLI fournit une expérience efficace et de bas niveau qui fonctionne sur tous les systèmes d’exploitation pris en charge. Vous pouvez toujours créer des bibliothèques avec Visual Studio, et si c’est ce que vous préférez, consultez le guide Visual Studio.
Prérequis
Le SDK .NET doit être installé sur votre ordinateur.
Pour accéder aux sections de ce document concernant les versions du .NET Framework, vous devez installer le .NET Framework sur un ordinateur Windows.
Par ailleurs, si vous souhaitez prendre en charge des cibles .NET Framework plus anciennes, vous devez installer des packs de ciblage ou de développement à partir de la page de téléchargements de .NET Framework. Reportez-vous au tableau suivant :
Version du .NET Framework | À télécharger |
---|---|
4.6.1 | Pack de ciblage .NET Framework 4.6.1 |
4.6 | Pack de ciblage .NET Framework 4.6 |
4.5.2 | Pack du développeur .NET Framework 4.5.2 |
4.5.1 | Pack du développeur .NET Framework 4.5.1 |
4.5 | SDK Windows pour Windows 8 |
4.0 | SDK pour Windows 7 et .NET Framework 4 |
2.0, 3.0 et 3.5 | Runtime .NET Framework 3.5 SP1 (ou version Windows 8+) |
Comment cibler .NET 5+ ou .NET Standard
Vous contrôlez le framework cible de votre projet en l’ajoutant à votre fichier projet (.csproj ou .fsproj). Pour obtenir des conseils sur la façon de choisir entre le ciblage de .NET 5+ ou .NET Standard, consultez .NET 5+ et .NET Standard.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
</Project>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
</Project>
Si vous voulez cibler les versions .NET Framework 4.0 ou antérieures, ou que vous voulez utiliser une API disponible dans le .NET Framework, mais pas dans .NET Standard (par exemple, System.Drawing
), lisez les sections suivantes et découvrez comment réaliser un multiciblage.
Comment cibler .NET Framework
Notes
Les instructions suivantes supposent que le .NET Framework est installé sur votre ordinateur. Reportez-vous aux Prérequis pour installer les dépendances.
N’oubliez pas que certaines des versions du .NET Framework utilisées ici ne sont plus prises en charge. Reportez-vous au Forum aux questions sur la politique de support de Microsoft .NET Framework concernant les versions non prises en charge.
Si vous voulez atteindre le nombre maximal de développeurs et de projets, utilisez .NET Framework 4.0 comme cible de base de référence. Pour cibler .NET Framework, commencez par utiliser le moniker de framework cible approprié qui correspond à la version de.NET Framework que vous voulez prendre en charge.
Version du .NET Framework | TFM |
---|---|
.NET Framework 2.0 | net20 |
.NET Framework 3.0 | net30 |
.NET Framework 3.5 | net35 |
.NET Framework 4.0 | net40 |
.NET Framework 4.5 | net45 |
.NET Framework 4.5.1 | net451 |
.NET Framework 4.5.2 | net452 |
.NET Framework 4.6 | net46 |
.NET Framework 4.6.1 | net461 |
.NET Framework 4.6.2 | net462 |
.NET Framework 4.7 | net47 |
.NET Framework 4.8 | net48 |
Insérez ensuite ce Moniker du Framework cible dans la section TargetFramework
de votre fichier projet. Par exemple, voici comment écrire une bibliothèque qui cible .NET Framework 4.0 :
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net40</TargetFramework>
</PropertyGroup>
</Project>
C’est tout ! Bien que ce code se compilait uniquement pour .NET Framework 4, vous pouvez utiliser la bibliothèque sur des versions plus récentes de .NET Framework.
Comment réaliser un multiciblage
Notes
Les instructions suivantes supposent que le .NET Framework est installé sur votre ordinateur. Reportez-vous à la section Prérequis pour savoir quelles dépendances doivent être installées et à partir d’où les télécharger.
Vous devrez peut-être cibler des versions antérieures de .NET Framework lorsque votre projet prend en charge .NET Framework et .NET. Dans ce scénario, si vous voulez utiliser des API et des constructions de langage plus récentes pour des cibles plus récentes, utilisez des directives #if
dans votre code. Vous pouvez aussi avoir besoin d’ajouter différents packages et différentes dépendances pour chaque plateforme que vous ciblez pour inclure les différentes API nécessaires à chaque cas.
Supposons, par exemple, que vous avez une bibliothèque qui effectue des opérations réseau sur HTTP. Pour .NET Standard et les versions .NET Framework 4.5 ou ultérieures, vous pouvez utiliser la classe HttpClient
à partir de l’espace de noms System.Net.Http
. Toutefois, les versions antérieures du .NET Framework ne disposent pas de la classe HttpClient
. Vous pourriez donc utiliser la classe WebClient
à partir de l’espace de noms System.Net
à la place.
Voici à quoi peut ressembler votre fichier projet :
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net40;net45</TargetFrameworks>
</PropertyGroup>
<!-- Need to conditionally bring in references for the .NET Framework 4.0 target -->
<ItemGroup Condition="'$(TargetFramework)' == 'net40'">
<Reference Include="System.Net" />
</ItemGroup>
<!-- Need to conditionally bring in references for the .NET Framework 4.5 target -->
<ItemGroup Condition="'$(TargetFramework)' == 'net45'">
<Reference Include="System.Net.Http" />
<Reference Include="System.Threading.Tasks" />
</ItemGroup>
</Project>
Notez trois changements majeurs ici :
- Le nœud
TargetFramework
a été remplacé parTargetFrameworks
, et trois Monikers du Framework cible sont exprimés à l’intérieur. - Un nœud
<ItemGroup>
est présent pour l’extraction de ciblenet40
dans une référence .NET Framework. - Un nœud
<ItemGroup>
est présent pour l’extraction de ciblenet45
dans deux références .NET Framework.
Symboles du préprocesseur
Le système de build est informé des symboles de préprocesseur suivants utilisés dans les directives #if
:
Versions cibles de .NET Framework | symboles | Symboles supplémentaires (disponibles dans les SDK .NET 5+) |
Symboles de plateforme (disponibles uniquement lorsque vous spécifiez un moniker de framework cible spécifique au système d’exploitation) |
---|---|---|---|
.NET Framework | NETFRAMEWORK , NET48 , NET472 , NET471 , NET47 , NET462 , NET461 , NET46 , NET452 , NET451 , NET45 , NET40 , NET35 , NET20 |
NET48_OR_GREATER , NET472_OR_GREATER , NET471_OR_GREATER , NET47_OR_GREATER , NET462_OR_GREATER , NET461_OR_GREATER , NET46_OR_GREATER , NET452_OR_GREATER , NET451_OR_GREATER , NET45_OR_GREATER , NET40_OR_GREATER , NET35_OR_GREATER , NET20_OR_GREATER |
|
.NET Standard | NETSTANDARD , NETSTANDARD2_1 , NETSTANDARD2_0 , NETSTANDARD1_6 , NETSTANDARD1_5 , NETSTANDARD1_4 , NETSTANDARD1_3 , NETSTANDARD1_2 , NETSTANDARD1_1 , NETSTANDARD1_0 |
NETSTANDARD2_1_OR_GREATER , NETSTANDARD2_0_OR_GREATER , NETSTANDARD1_6_OR_GREATER , NETSTANDARD1_5_OR_GREATER , NETSTANDARD1_4_OR_GREATER , NETSTANDARD1_3_OR_GREATER , NETSTANDARD1_2_OR_GREATER , NETSTANDARD1_1_OR_GREATER , NETSTANDARD1_0_OR_GREATER |
|
.NET 5+ (et .NET Core) | NET , NET8_0 , NET7_0 , NET6_0 , NET5_0 , NETCOREAPP , NETCOREAPP3_1 , NETCOREAPP3_0 , NETCOREAPP2_2 , NETCOREAPP2_1 , NETCOREAPP2_0 , NETCOREAPP1_1 , NETCOREAPP1_0 |
NET8_0_OR_GREATER , NET7_0_OR_GREATER , NET6_0_OR_GREATER , NET5_0_OR_GREATER , NETCOREAPP3_1_OR_GREATER , NETCOREAPP3_0_OR_GREATER , NETCOREAPP2_2_OR_GREATER , NETCOREAPP2_1_OR_GREATER , NETCOREAPP2_0_OR_GREATER , NETCOREAPP1_1_OR_GREATER , NETCOREAPP1_0_OR_GREATER |
ANDROID , BROWSER , IOS , MACCATALYST , MACOS , TVOS , WINDOWS ,[OS][version] (par exemple, IOS15_1 ),[OS][version]_OR_GREATER (par exemple IOS15_1_OR_GREATER ) |
Notes
- Les symboles sans version sont définis indépendamment de la version que vous ciblez.
- Les symboles spécifiques à la version sont définis uniquement pour la version que vous ciblez.
- Les symboles
<framework>_OR_GREATER
sont définis pour la version que vous ciblez et toutes les versions antérieures. Par exemple, si vous ciblez .NET Framework 2.0, les symboles suivants sont définis :NET20
,NET20_OR_GREATER
,NET11_OR_GREATER
etNET10_OR_GREATER
. - Les symboles
NETSTANDARD<x>_<y>_OR_GREATER
sont définis uniquement pour les cibles .NET Standard, et non pour les cibles qui implémentent .NET Standard, telles que .NET Core et .NET Framework. - Ils sont différents des monikers de framework cible utilisés par la propriété MSBuild
TargetFramework
et NuGet.
Voici un exemple d’utilisation de la compilation conditionnelle par cible :
using System;
using System.Text.RegularExpressions;
#if NET40
// This only compiles for the .NET Framework 4 targets
using System.Net;
#else
// This compiles for all other targets
using System.Net.Http;
using System.Threading.Tasks;
#endif
namespace MultitargetLib
{
public class Library
{
#if NET40
private readonly WebClient _client = new WebClient();
private readonly object _locker = new object();
#else
private readonly HttpClient _client = new HttpClient();
#endif
#if NET40
// .NET Framework 4.0 does not have async/await
public string GetDotNetCount()
{
string url = "https://www.dotnetfoundation.org/";
var uri = new Uri(url);
string result = "";
// Lock here to provide thread-safety.
lock(_locker)
{
result = _client.DownloadString(uri);
}
int dotNetCount = Regex.Matches(result, ".NET").Count;
return $"Dotnet Foundation mentions .NET {dotNetCount} times!";
}
#else
// .NET Framework 4.5+ can use async/await!
public async Task<string> GetDotNetCountAsync()
{
string url = "https://www.dotnetfoundation.org/";
// HttpClient is thread-safe, so no need to explicitly lock here
var result = await _client.GetStringAsync(url);
int dotNetCount = Regex.Matches(result, ".NET").Count;
return $"dotnetfoundation.org mentions .NET {dotNetCount} times in its HTML!";
}
#endif
}
}
Si vous générez ce projet avec dotnet build
, notez la présence de trois répertoires sous le dossier bin/
:
net40/
net45/
netstandard2.0/
Chacun d’entre eux contient les fichiers .dll
pour chaque cible.
Comment tester des bibliothèques sur .NET
Il est important de pouvoir effectuer des tests sur plusieurs plateformes. Vous pouvez utiliser xUnit ou MSTest dans leur version d’origine. Les deux conviennent parfaitement à la réalisation de tests unitaires sur votre bibliothèque sur .NET. La façon dont vous configurez votre solution avec des projets de test dépend de la structure de votre solution. L’exemple suivant part du principe que les répertoires de test et source résident dans le même répertoire de premier niveau.
Notes
Cet exemple utilise certaines commandes de l’interface CLI .NET. Pour plus d’informations, consultez dotnet new et dotnet sln.
Configurez votre solution. Pour cela, utilisez la commande suivante :
mkdir SolutionWithSrcAndTest cd SolutionWithSrcAndTest dotnet new sln dotnet new classlib -o MyProject dotnet new xunit -o MyProject.Test dotnet sln add MyProject/MyProject.csproj dotnet sln add MyProject.Test/MyProject.Test.csproj
Cette commande crée des projets et les relie dans une solution. Votre répertoire pour
SolutionWithSrcAndTest
doit ressembler à ceci :/SolutionWithSrcAndTest |__SolutionWithSrcAndTest.sln |__MyProject/ |__MyProject.Test/
Accédez au répertoire du projet de test et ajoutez une référence à
MyProject.Test
à partir deMyProject
.cd MyProject.Test dotnet add reference ../MyProject/MyProject.csproj
Restaurez les packages et générez les projets :
dotnet restore dotnet build
Exécutez la commande
dotnet test
pour vérifier que xUnit s’exécute. Si vous avez choisi d’utiliser MSTest, le Test Runner de console MSTest doit s’exécuter à la place.
C’est tout ! Vous pouvez maintenant tester votre bibliothèque sur toutes les plateformes à l’aide des outils en ligne de commande. Maintenant que vous avez tout configuré, le test de votre bibliothèque est très simple :
- Apportez des modifications à votre bibliothèque.
- Exécutez les tests à partir de la ligne de commande, dans votre répertoire de test, avec la commande
dotnet test
.
Votre code est automatiquement régénéré quand vous appelez la commande dotnet test
.
Comment utiliser plusieurs projets
Les bibliothèques plus volumineuses ont souvent besoin de placer des fonctionnalités dans différents projets.
Imaginez que vous vouliez créer une bibliothèque pouvant être utilisée en langage C# et F#. Cela signifierait que les consommateurs de votre bibliothèque les utilisent dans des formes qui sont naturelles en C# ou F#. Par exemple, en C#, vous pouvez utiliser la bibliothèque de la façon suivante :
using AwesomeLibrary.CSharp;
public Task DoThings(Data data)
{
var convertResult = await AwesomeLibrary.ConvertAsync(data);
var result = AwesomeLibrary.Process(convertResult);
// do something with result
}
En F#, le code peut se présenter comme suit :
open AwesomeLibrary.FSharp
let doWork data = async {
let! result = AwesomeLibrary.AsyncConvert data // Uses an F# async function rather than C# async method
// do something with result
}
Les scénarios de consommation tels que celui-ci signifient que les API auxquelles vous accédez ont une structure différente en C# et F#. Pour réaliser ceci, il est courant de factoriser toute la logique d’une bibliothèque dans un projet principal et d’avoir des projets C# et F# définissant les couches API qui effectuent des appels à ce projet principal. Le reste de la section utilise les noms suivants :
- AwesomeLibrary.Core : projet principal qui contient toute la logique de la bibliothèque
- AwesomeLibrary.CSharp : projet avec des API publiques destinées à être consommées en C#
- AwesomeLibrary.CSharp : projet avec des API publiques destinées à être consommées en F#
Vous pouvez exécuter les commandes suivantes dans votre terminal pour produire la même structure que ce guide :
mkdir AwesomeLibrary && cd AwesomeLibrary
dotnet new sln
mkdir AwesomeLibrary.Core && cd AwesomeLibrary.Core && dotnet new classlib
cd ..
mkdir AwesomeLibrary.CSharp && cd AwesomeLibrary.CSharp && dotnet new classlib
cd ..
mkdir AwesomeLibrary.FSharp && cd AwesomeLibrary.FSharp && dotnet new classlib -lang "F#"
cd ..
dotnet sln add AwesomeLibrary.Core/AwesomeLibrary.Core.csproj
dotnet sln add AwesomeLibrary.CSharp/AwesomeLibrary.CSharp.csproj
dotnet sln add AwesomeLibrary.FSharp/AwesomeLibrary.FSharp.fsproj
Cette opération ajoute les trois projets ci-dessus et un fichier solution qui les relie. Le fait de créer le fichier solution et de lier des projets vous permet de restaurer et de générer des projets à partir d’un niveau supérieur.
Références entre projets
La meilleure façon de référencer un projet consiste à utiliser l’interface de ligne de commande .NET pour ajouter une référence de projet. À partir des répertoires de projet AwesomeLibrary.CSharp et de AwesomeLibrary.FSharp, vous pouvez exécuter la commande suivante :
dotnet add reference ../AwesomeLibrary.Core/AwesomeLibrary.Core.csproj
Les fichiers projet pour AwesomeLibrary.CSharp et AwesomeLibrary.FSharp référencent désormais AwesomeLibrary.Core comme cible de ProjectReference
. Pour le vérifier, confirmez la présence des éléments suivants dans les fichiers projet :
<ItemGroup>
<ProjectReference Include="..\AwesomeLibrary.Core\AwesomeLibrary.Core.csproj" />
</ItemGroup>
Vous pouvez ajouter manuellement cette section à chaque fichier projet si vous préférez ne pas utiliser l’interface de ligne de commande .NET.
Structurer une solution
Un autre aspect important des solutions à projets multiples est la mise en place d’une bonne structure de projet globale. Vous pouvez organiser le code comme vous le souhaitez, et tant que vous liez chaque projet à votre fichier solution avec dotnet sln add
, vous pouvez exécuter dotnet restore
et dotnet build
au niveau de la solution.