C++/CLI封装.NET库供原生C+ +项目调用

以一个实例详细介绍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";
}


/// <summary>
/// 定义一个静态方法,进行Json序列化
/// </summary>
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");
}

评论