以一个实例详细介绍C+ +/CLI封装.NET库供原生C+ +项目调用的步骤。
已得结论
在前面文章《C+ +/CLI基本语法和最佳实践》中,已经得到了以下重要结论:
(1)struct / class
- 无法定义托管类型的字段。
- 方法的参数和返回值可以为托管类型。
(2)封装程序库供C+ +使用:
- 在C+ +/CLI的头文件中不能包含任何托管代码特性。
- 封装C#代码给原生C+ +项目使用:在C+ +/CLI的导出类方法成员或导出函数内部使用C#的静态方法,或实例化C#类,从而使用既有C#的代码。
下面以一个完整实例详细介绍如何使用C+ +/CLI封装.NET库供原生C+ +项目调用。
C#程序
以下C#代码定义了MyToolkits类,类中包含三个实例方法Add、Sum和IsStringRight,并定义了一个静态方法Fun01用于对Person数组进行json序列化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| using Newtonsoft.Json; using System; using System.Collections.Generic;
namespace CSLibrary { public class MyToolkits { public double Add(double a, double b) { return a + b; }
public double Sum(List<double> list) { double sum = 0; foreach (var item in list) { sum += item; } return sum; }
public bool IsStringRight(string text) { return text == "LH"; }
public static void Fun01() { List<Person> persons = new List<Person>(); persons.Add(new Person("li", "hao", 30, "lh@123.com")); persons.Add(new Person("li", "hao", 30, "lh@123.com")); persons.Add(new Person("li", "hao", 30, "lh@123.com")); string json = JsonConvert.SerializeObject(persons, Newtonsoft.Json.Formatting.Indented); Console.WriteLine(json); } }
internal class Person { [JsonProperty("first_name")] public string FirstName { get; set; }
[JsonProperty("last_name")] public string LastName { get; set; }
public int Age { get; set; }
public string Email { get; set; }
public Person(string firstName, string lastName, int age, string email) { FirstName = firstName; LastName = lastName; Age = age; Email = email; } } }
|
使用C+ +/CLI封装
头文件
以下是C+ +/CLI封装库的头文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #pragma once #include <vector> #include <string>
#ifdef MYDLL_EXPORTS #define MYDLL_API __declspec(dllexport) #else #define MYDLL_API __declspec(dllimport) #endif
namespace lh2 { class MYDLL_API MyFuncs { public: double Add(double a, double b); double Sum(const std::vector<double>& data); bool IsStringRight(std::string text);
static void Fun01(); }; }
|
结合前面的重要结论:
- 头文件中不包含任何的托管代码特性。
- 在Add、Sum、IsStringRight实例方法中,先实例化MyToolkits托管类,再调用托管类的方法进行计算。
- C+ +/CLI中的double类型可以直接转换成Double托管类型,无需手动转换;原生C+ +的vector转托管List,采用遍历赋值的方式;原生C+ +的string转托管String类型,使用官方提供的转换函数。
- 这里的Fun01静态函数实现时,在方法体直接调用C#的静态函数。
源文件
以下是C+ +/CLI封装库的源文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| #include <msclr/marshal_cppstd.h> #include "MyToolkitsWrapper.h"
using namespace CSLibrary; using namespace System; using namespace System::Collections::Generic;
namespace lh2 { double MyFuncs::Add(double a, double b) { MyToolkits^ tool = gcnew MyToolkits(); return tool->Add(a, b); }
double MyFuncs::Sum(const std::vector<double>& data) { List<Double>^ list = gcnew List<Double>(); for each (double d in data) { list->Add(d); } MyToolkits^ tool = gcnew MyToolkits(); return tool->Sum(list); }
bool MyFuncs::IsStringRight(std::string text) { MyToolkits^ tool = gcnew MyToolkits(); return tool->IsStringRight(msclr::interop::marshal_as<String^>(text)); }
void MyFuncs::Fun01() { MyToolkits::Fun01(); } }
|
在C+ +项目中使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #include "MyToolkitsWrapper.h" #include <iostream> using namespace lh2;
int main() { MyFuncs fun; std::cout << fun.Add(12, 30) << std::endl;
std::vector<double> data{ 1,2,3,4,5,6,7,8,9,10 }; std::cout << fun.Sum(data) << std::endl;
std::cout << fun.IsStringRight("LH") << std::endl; std::cout << fun.IsStringRight("LX") << std::endl;
fun.Fun01();
system("pause"); }
|
