1. 现在在Windows下的应用程序开发,VS.Net占据了绝大多数的份额。因此很多以前搞VC++开发的人都转向用更强大的VS.Net。在这种情况下,有很多开发人员就面临了如何在C#中使用C++开发好的类的问题。下面就用一个完整的实例来详细说明怎样用托管C++封装一个C++类以提供给C#使用。
  2. 比如,现在有一个工程名为NativeCppDll的由C++编写的DLL,里面输出了一个CPerson类。下面是具体的代码:
  3.  
  4. // NativeCppDll.h
  5. #pragma once
  6. #ifndef LX_DLL_CLASS_EXPORTS
  7. #define LX_DLL_CLASS __declspec(dllexport)
  8. #else
  9. #define LX_DLL_CLASS __declspec(dllimport)
  10. #endif
  11. class LX_DLL_CLASS CPerson
  12. {
  13. public:
  14. CPerson();
  15. CPerson(const wchar_t *pName, const wchar_t cSex, int iAge);
  16. void SetName(const wchar_t *pName);
  17. wchar_t * GetName();
  18. void SetSex(const wchar_t cSex);
  19. wchar_t GetSex();
  20. void SetAge(int iAge);
  21. int GetAge();
  22. wchar_t * GetLastError();
  23. private:
  24. wchar_t m_szName[];
  25. wchar_t m_cSex;
  26. int m_iAge;
  27. wchar_t m_szLastError[];
  28. void ShowError();
  29. };
  30. // NativeCppDll.cpp
  31. #include "stdafx.h"
  32. #include "NativeCppDll.h"
  33. #include <iostream>
  34. #include <tchar.h>
  35. using namespace std;
  36. CPerson::CPerson()
  37. {
  38. wcscpy_s(m_szName, _T("No Name"));
  39. m_cSex = 'N';
  40. m_iAge = ;
  41. wcscpy_s(m_szLastError, _T("No Error"));
  42. }
  43. CPerson::CPerson(const wchar_t *pName, const wchar_t cSex, int iAge)
  44. {
  45. wcscpy_s(m_szLastError, _T("No Error"));
  46. SetName(pName);
  47. SetSex(cSex);
  48. SetAge(iAge);
  49. }
  50. void CPerson::SetName(const wchar_t *pName)
  51. {
  52. if ((pName == NULL) || (wcslen(pName) == ) || (wcslen(pName) > ))
  53. {
  54. wcscpy_s(m_szName, _T("No Name"));
  55. wcscpy_s(m_szLastError, _T("The length of the input name is out of range."));
  56. ShowError();
  57. return;
  58. }
  59. wcscpy_s(m_szName, pName);
  60. }
  61. wchar_t * CPerson::GetName()
  62. {
  63. return m_szName;
  64. }
  65. void CPerson::SetSex(const wchar_t cSex)
  66. {
  67. if ((cSex != 'F') && (cSex != 'M') && (cSex != 'm') && (cSex != 'f'))
  68. {
  69. m_cSex = 'N';
  70. wcscpy_s(m_szLastError, _T("The input sex is out of [F/M]."));
  71. ShowError();
  72.  
  73. return;
  74. }
  75. m_cSex = cSex;
  76. }
  77. wchar_t CPerson::GetSex()
  78. {
  79. return m_cSex;
  80. }
  81. void CPerson::SetAge(int iAge)
  82. {
  83. if ((iAge < ) || (iAge > ))
  84. {
  85. m_iAge = ;
  86. wcscpy_s(m_szLastError, _T("The input age is out of range."));
  87. ShowError();
  88. return;
  89. }
  90. m_iAge = iAge;
  91. }
  92. int CPerson::GetAge()
  93. {
  94. return m_iAge;
  95. }
  96. wchar_t * CPerson::GetLastError()
  97. {
  98. return m_szLastError;
  99. }
  100. void CPerson::ShowError()
  101. {
  102. cerr << m_szLastError << endl;
  103. }
  104. 这是一个很典型的由C++开发的DLL,输出一个完整的C++类。如果现在要求开发一个C#工程,需要用到这个DLL中输出的C++类CPerson,该怎么办呢?针对这个例子来说,类CPerson非常小,可以用C#重新写一个跟这个C++类一样的类。可是,如果需要的C++类很大,或者很多的时候,重写工程将非常庞大。而且这样没有对现有的代码进行重用,浪费了现有资源,开发起来费时费力。
  105. 当然,还是有方法解决这个问题的。那就是用托管C++将C++类给封装一下,然后再提供给C#来使用。下面就用代码来详细说明怎样用托管C++来封装上面的那个C++类。
  106. 首先,要创建一个托管C++的DLL工程ManageCppDll,然后在里面添加下面的代码:
  107.  
  108. // ManageCppDll.h
  109. #pragma once
  110. #define LX_DLL_CLASS_EXPORTS
  111. #include "../NativeCppDll/NativeCppDll.h"
  112. using namespace System;
  113. namespace ManageCppDll
  114. {
  115. public ref class Person
  116. {
  117. // 包装所有类CPerson的公有成员函数
  118. public:
  119. Person();
  120. Person(String ^ strName, Char cSex, int iAge);
  121. ~Person();
  122. property String ^ Name
  123. {
  124. void set(String ^ strName);
  125. String ^ get();
  126. }
  127. property Char Sex
  128. {
  129. void set(Char cSex);
  130. Char get();
  131. }
  132. property int Age
  133. {
  134. void set(int iAge);
  135. int get();
  136. }
  137. String ^ GetLastError();
  138. private:
  139. // 类CPerson的指针,用来调用类CPerson的成员函数
  140. CPerson *m_pImp;
  141. };
  142. };
  143. 从这个头文件就能看出来,这是对C++类CPerson的包装。类Person的所有公有成员函数都跟C++类CPerson一样,只不过成员函数的参数和返回值就改成了托管C++的类型,这也是让类Person能在C#中使用的首要条件。当然只需要对公有成员函数进行封装,对于保护成员函数和私有成员函数则不必做任何封装。
  144. Person仅有一个私有的成员变量:一个类CPerson的指针。而类Person的所有成员函数的实现都是靠这个CPerson指针来调用类CPerson的相应成员函数来实现。
  145. 下面是具体的实现代码:
  146.  
  147. // ManageCppDll.cpp
  148. #include "stdafx.h"
  149. #include "ManageCppDll.h"
  150. #include <vcclr.h>
  151. namespace ManageCppDll
  152. {
  153. // 在构造函数中创建类CPerson的对象并在析构函数中将该对象销毁
  154. // 所有的成员函数实现都是通过指针m_pImp调用类CPerson的相应成员函数实现
  155. Person::Person()
  156. {
  157. m_pImp = new CPerson();
  158. }
  159. Person::Person(String ^ strName, Char cSex, int iAge)
  160. {
  161. // 将string转换成C++能识别的指针
  162. pin_ptr<const wchar_t> wcName = PtrToStringChars(strName);
  163. m_pImp = new CPerson(wcName, cSex, iAge);
  164. }
  165. Person::~Person()
  166. {
  167. // 在析构函数中删除CPerson对象
  168. delete m_pImp;
  169. }
  170. void Person::Name::set(String ^ strName)
  171. {
  172. pin_ptr<const wchar_t> wcName = PtrToStringChars(strName);
  173. m_pImp->SetName(wcName);
  174. }
  175. String ^ Person::Name::get()
  176. {
  177. return gcnew String(m_pImp->GetName());
  178. }
  179. void Person::Sex::set(Char cSex)
  180. {
  181. m_pImp->SetSex(cSex);
  182. }
  183. Char Person::Sex::get()
  184. {
  185. return m_pImp->GetSex();
  186. }
  187. void Person::Age::set(int iAge)
  188. {
  189. m_pImp->SetAge(iAge);
  190. }
  191. int Person::Age::get()
  192. {
  193. return m_pImp->GetAge();
  194. }
  195. String ^ Person::GetLastError()
  196. {
  197. return gcnew String(m_pImp->GetLastError());
  198. }
  199. };
  200. 如果要在C#中使用类Person,首先要添加对ManageCppDll.dll的引用,然后就可以像用普通的C#类一样的使用类Person了。比如下面这样的代码:
  201.  
  202. using ManageCppDll;
  203. Person person = new Person();
  204. person.Name = "StarLee";
  205. person.Sex = 'M';
  206. person.Age = ;
  207. 熟悉设计模式的看了上面的代码肯定会发现,这样的设计跟BRIDGE模式如出一辙。其实,上面的方法也算是一种BRIDGE模式,由托管C++充当了C#中使用用C++开发的类的桥梁。另外,这种形式也可以理解为ADAPTER模式,托管C++类Person就是C++类CPerson的一个适配器。通过这个桥梁,可以很容易的重用以前用C++开发的类,让这些C++类继续在C#中发挥它们的效用,让开发变得事半功倍。

在C#中使用C++编写的类的更多相关文章

  1. 在C++中使用C#编写的类2

    在那篇<在C#中使用C++编写的类>中我介绍了如何在C#中使用C++编写的类.可是由于C#在用户界面设计.数据库存储和XML文件读取等方面的优势,有时候也会出现要在C++中使用C#编写的类 ...

  2. 在C#中使用C++编写的类1

    转载地址:http://blog.csdn.net/starlee/article/details/2864588 现在在Windows下的应用程序开发,VS.Net占据了绝大多数的份额.因此很多以前 ...

  3. 在C#中使用C++编写的类——用托管C++进行封装[转]

    现在在Windows下的应用程序开发,VS.Net占据了绝大多数的份额.因此很多以前搞VC++开发的人都转向用更强大的VS.Net.在这种情况 下,有很多开发人员就面临了如何在C#中使用C++开发好的 ...

  4. 在C#中使用C++编写的类——用托管C++进行封装

    现在在Windows下的应用程序开发,VS.Net占据了绝大多数的份额.因此很多以前搞VC++开发的人都转向用更强大的VS.Net.在这种情况下,有很多开发人员就面临了如何在C#中使用C++开发好的类 ...

  5. 22.编写一个类A,该类创建的对象可以调用方法showA输出小写的英文字母表。然后再编写一个A类的子类B,子类B创建的对象不仅可以调用方法showA输出小写的英文字母表,而且可以调用子类新增的方法showB输出大写的英文字母表。最后编写主类C,在主类的main方法 中测试类A与类B。

    22.编写一个类A,该类创建的对象可以调用方法showA输出小写的英文字母表.然后再编写一个A类的子类B,子类B创建的对象不仅可以调用方法showA输出小写的英文字母表,而且可以调用子类新增的方法sh ...

  6. 编写高质量代码改善C#程序的157个建议——建议112:将现实世界中的对象抽象为类,将可复用对象圈起来就是命名空间

    建议112:将现实世界中的对象抽象为类,将可复用对象圈起来就是命名空间 在我们身边的世界中,对象是什么?对象就是事物,俗称“东西”.那么,什么东西算得上是一个对象呢?对象有属性.有行为.以动物为例,比 ...

  7. 题目一:编写一个类Computer,类中含有一个求n的阶乘的方法

    作业:编写一个类Computer,类中含有一个求n的阶乘的方法.将该类打包,并在另一包中的Java文件App.java中引入包,在主类中定义Computer类的对象,调用求n的阶乘的方法(n值由参数决 ...

  8. 35.按要求编写Java程序: (1)编写一个接口:InterfaceA,只含有一个方法int method(int n); (2)编写一个类:ClassA来实现接口InterfaceA,实现int method(int n)接口方 法时,要求计算1到n的和; (3)编写另一个类:ClassB来实现接口InterfaceA,实现int method(int n)接口 方法时,要求计算n的阶乘(n

      35.按要求编写Java程序: (1)编写一个接口:InterfaceA,只含有一个方法int method(int n): (2)编写一个类:ClassA来实现接口InterfaceA,实现in ...

  9. C++中public,protected,private派生类继承问题和访问权限问题

    C++中public,protected,private派生类继承问题和访问权限问题 当一个子类从父类继承时,父类的所有成员成为子类的成员,此时对父类成员的访问状态由继承时使用的继承限定符决定. 1. ...

随机推荐

  1. exec函数族实例解析

    exec函数族实例解析 fork()函数通过系统调用创建一个与原来进程(父进程)几乎完全相同的进程(子进程是父进程的副本,它将获得父进程数据空间.堆.栈等资源的副本.注意,子进程持有的是上述存储空间的 ...

  2. SPSS数据分析—分段回归

    在SPSS非线性回归过程中,我们讲到了损失函数按钮可以自定义损失函数,但是还有一个约束按钮没有讲到,该按钮的功能是对自 定义的损失函数的参数设定条件,这些条件通常是由逻辑表达式组成,这就使得损失函数具 ...

  3. Code Simplicity–The Science of Software Development 书摘

    Chapter1 Introduction That is the art and talent involved in programming—reducing complexity to simp ...

  4. python jar

    jpype不好用 pyjnius pip install jnius pip install cython

  5. C#/ASP.NET MVC微信公众号接口开发之从零开发(二) 接收微信消息并且解析XML(附源码)

    文章导读: C#微信公众号接口开发之从零开发(一) 接入微信公众平台 微信接入之后,微信通过我们接入的地址进行通信,其中的原理是微信用户发送消息给微信公众账号,微信服务器将消息以xml的形式发送到我们 ...

  6. 常用的获取时间差的sql语句

    "select count(*) from [注册] where datediff(day,time,getdate())<1";//获取当天注册人员数 sql=" ...

  7. IT公司100题-35- 求一个矩阵中最大的二维矩阵(元素和最大)

    问题描述: 求一个矩阵中最大的二维矩阵(元素和最大).如: 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 中最大的是: 4 5 9 10   分析: 2*2子数组的最大和.遍历求和,时 ...

  8. ESXi虚拟磁盘共享

    因为项目需要需要一个磁盘共享的环境. 最先想到用iSCSI,两个客户端挂载同一个远端盘:这样确实可行,但是感觉太繁琐,想到既然是虚拟机环境,可以设置虚拟磁盘共享. 于是网上一番搜罗,确实有人这个做过, ...

  9. maven3.04管理jetty9.2.10启动web项目

    在pom.xml文件中添加如下: <build>    <pluginManagement>        <plugins>            <plu ...

  10. mysql之存储过程

    一.存储过程     迄今为止,使用的大多数 SQL语句都是针对一个或多个表的单条语句.并非所有操作都这么简单,经常会有一个完整的操作需要多条语句才能完成.例如,考虑以下的情形.         1. ...