Pourquoi C# ne vit pas dans un vase clos, et comment cela fonctionne
\n\nQuand j'ai commencé à travailler avec C# il y a une dizaine d'années, je pensais que .NET était un univers autonome. Bibliothèques, frameworks, bases de données — tout était à portée de main. Mais ensuite est venue une tâche : il fallait utiliser une ancienne bibliothèque C++ pour le traitement du signal. Et là, j'ai compris — l'intégration de C# avec d'autres langages n'est pas un luxe, mais une nécessité pour tout projet sérieux.
\n\nAujourd'hui, C# est un langage multiplateforme avec un riche écosystème, mais il ne peut pas tout faire. Parfois, il faut exécuter un script Python pour le machine learning, parfois appeler une fonction native de Rust pour des performances maximales, et parfois intégrer un moteur JavaScript pour la génération dynamique d'interface utilisateur. Bonne nouvelle : C# sait s'entendre avec pratiquement tout le monde.
\n\nDans cet article, je vais montrer des scénarios réels d'interaction de C# avec d'autres langages avec du code fonctionnel, des benchmarks et des pièges à éviter. C'est parti !
\n\nP/Invoke : le classique pour C et C++
\n\nPlatform Invocation Services (P/Invoke) est un mécanisme intégré de .NET pour appeler des fonctions à partir de DLL natives. Il existe depuis la première version de .NET et reste le moyen le plus rapide d'intégration avec du code C/C++.
\n\nExemple de base : appel d'une fonction depuis C
\n\nSupposons que nous ayons une bibliothèque native math_helper.dll avec une fonction :
// math_helper.h\nextern "C" __declspec(dllexport) double calculate_power(double base, int exp);\n\nEn C#, nous l'importons ainsi :
\n\nusing System.Runtime.InteropServices;\n\npublic class NativeMath\n{\n [DllImport("math_helper.dll", CallingConvention = CallingConvention.Cdecl)]\n public static extern double calculate_power(double base, int exp);\n}\n\n// Utilisation\ndouble result = NativeMath.calculate_power(2.0, 10);\nConsole.WriteLine($"2^10 = {result}"); // 1024\n\nPoints importants pour P/Invoke C# :
\n- \n
- Indiquez toujours
CallingConvention— le plus souventCdeclouStdCall. \n - Pour les chaînes, utilisez
MarshalAs(UnmanagedType.LPStr)ouLPWStr. \n - Passez les structures par référence avec
refouout. \n
P/Invoke a une surcharge minimale — littéralement quelques nanosecondes par appel. Si vous avez besoin d'intégrer C# avec C++ pour des calculs à forte charge, c'est votre choix.
\n\nC++/CLI : le pont entre les mondes
\n\nC++/CLI est une extension de Visual C++ qui permet d'écrire du code fonctionnant à la fois dans un environnement managé (.NET) et non managé. Ce n'est pas un langage séparé, mais un hybride souvent utilisé pour créer des wrappers autour de bibliothèques C++.
\n\nExemple de wrapper pour OpenCV
\n\nSupposons que nous ayons une fonction C++ traitant une image :
\n\n// NativeImageProcessor.h\n#pragma once\npublic ref class ImageProcessor\n{\npublic:\n static System::Drawing::Bitmap^ ApplyFilter(System::Drawing::Bitmap^ bmp);\n};\n\nÀ l'intérieur, nous appelons OpenCV natif, et nous retournons un Bitmap managé. Depuis C#, nous l'utilisons comme une bibliothèque .NET ordinaire :
using ImageProcessorLib;\n\nvar img = new Bitmap("photo.jpg");\nvar processed = ImageProcessor.ApplyFilter(img);\nprocessed.Save("filtered.jpg");\n\nL'intégration C++/CLI offre un contrôle total sur la mémoire et permet d'éviter le marshaling. L'inconvénient est que le code devient dépendant de la plateforme et plus difficile à déboguer.
\n\nPython.NET : appeler Python depuis C#
\n\nPython est le roi de la Data Science et du machine learning. Et si votre projet en C# nécessite, par exemple, d'exécuter un réseau de neurones avec PyTorch ou de traiter du texte avec spaCy, l'appel de Python depuis C# via Python.NET est ce qu'il vous faut.
\n\nInstallation et premier exemple
\n\nInstallez le package :
\n\ndotnet add package Python.Runtime.NETStandard