在C++中反射调用.NET的方法(二)

反射调用返回复杂对象的.NET方法

定义数据接口

上一篇在C++中反射调用.NET(一)中,我们简单的介绍了如何使用C++/CLI并且初步使用了反射调用.NET程序集的简单方法,今天我们看看如何在C++与.NET程序集之间传递复杂对象。

先看看.NET程序集的一个返回对象的方法:

 public IUserInfo GetUserByID(int userId)
 {
  IUserInfo userinfo= EntityBuilder.CreateEntity<IUserInfo>();
  userinfo.ID = userId;
  userinfo.Name = "姓名_" + userId;
  userinfo.Birthday = new DateTime(1980, 1, 1);
  return userinfo;
 }

其中 IUserInfo是一个用户信息接口:

using System;
namespace NetLib
{
 public interface IUserInfo
 {
 DateTime Birthday { get; set; }
 int ID { get; set; }
 string Name { get; set; }
 }
}

接口内容很简单,有int,string,DateTime三种类型的属性,所以可以把它当做.NET与C++传递数据的DTO对象接口。

在方法 GetUserByID 中,有一行代码:

IUserInfo userinfo= EntityBuilder.CreateEntity<IUserInfo>();

EntityBuilder对象是PDF.NET SOD框架中的一个实体构造器,调用CreateEntity方法可以根据一个接口创建一个动态实体类对象,通过这种方式,我们可以不用去关心实体类的构造细节,仅仅关心方法调用的数据接口。在后面的示例中,我们都会通过这种接口对象的方式来传递数据。

绑定委托方法

下面我们来看看如何在C++/CLI中反射调用GetUserByID 这个方法。

虽然方法返回的是IUserInfo,但是对于我们的C++程序端来说,它并不知道IUserInfo这个接口对象,因为此接口没有在C++程序端定义,C++程序也没用引用它所在的.NET程序集,所以我们在反射调用GetUserByID 方法的时候,只能使用“弱类型”的Object,幸运的是我们调用的是返回值,而不是参数(反过来就不行,后面会有介绍),创建下面的委托对象是合法的:

Func<int, Object> fun;

详细的C++/CLI反射代码如下:

CppUserInfo GetUserByID(int userId)
 {
  //调用.NET方法,得到结果
  MethodInfo^ method = dotnetObject->GetType()->GetMethod("GetUserByID", BindingFlags::Public | BindingFlags::Instance);
  Func<int, Object^>^ fun = (Func<int, Object^>^)Delegate::CreateDelegate(Func<int, Object^>::typeid, this->dotnetObject, method);
  Object^ result = fun(userId);
  //转换托管类型数据到本机结构体
  Func<String^, Object^>^ entityProp =EntityHelper::EntityCallDelegate(result);
  CppUserInfo user;
  user.ID = (int)entityProp("ID");
  user.Name = (String^)entityProp("Name");// MarshalString((String^)entityProp("Name"));
  user.Birthday = Convert2CppDateTime((DateTime^)entityProp("Birthday"));
  return user;
 }

在上面的代码中,通过委托方法调用:

Object^ result = fun(userId); 

使用SOD DTO 对象

我们得到了.NET程序集的方法返回的DTO对象,但是如何取出它的数据赋值给我们的C++本机代码呢?

所以这里涉及到2个问题:

1,从Object对象取出数据;

2,将数据转换并且赋值给C++本地数据结构

对于第一个问题,我们可以反射DTO对象的属性,然后跟本地数据接口一一对应,但是,本来我们已经在反射调用方法了,再来一次反射事情就复杂了。

幸好,我们的DTO接口对象它是一个动态创建的SOD实体类对象,由于SOD实体类有类似“字典”的功能,可以通过相关方法进行访问。

实体类基类的一个方法定义:

public object PropertyList(string propertyFieldName)

我们反射此方法并且绑定一个委托对象来调用它:

 static Func<String^, Object^>^ EntityCallDelegate(Object^ entity)
 {
  //实体类基类的一个方法定义:
  //public object PropertyList(string propertyFieldName)
  Type^ base = entity->GetType()->BaseType;
  MethodInfo^ methodEntity = base->GetMethod("PropertyList", BindingFlags::Public | BindingFlags::Instance);
  Func<String^, Object^>^ funEntity = (Func<String^, Object^>^)Delegate::CreateDelegate(Func<String^, Object^>::typeid,
      entity, methodEntity);
  //示例 String^ result = (String^)funEntity("Name");
  return funEntity;
 }

然后,就能像下面这样使用了:

Func<String^, Object^>^ entityProp =EntityHelper::EntityCallDelegate(result);
int id = (int)entityProp("ID");

将.NET对象转换到C++结构体

在示例中,我们定义了一个CppUserInfo结构体:

struct CppUserInfo
{
 int ID;
 //wstring Name;
 CString Name;
 tm Birthday;
};

托管字符串与本机字符串

这个结构体跟C#版本的接口 IUserInfo对应,但是结构体成员有几个需要注意的地方:

CString Name;

字符串类型的“名字”成员,要在C++中使用字符串类型,必须在C++文件中包含下面的头文件:
如果不是 MFC应用程序,包含下面这个:

#include <atlstr.h>

否则,需要包含这个头文件:

#include <cstringt.h>  

如果不是使用CString,而是 wstring,那么需要定义一个方法来实现托管字符串到本机字符串的转换:

//
 //要使用下面的方法,请先 #include <string>
 //
 static wstring MarshalString(String ^ s) {
  wstring os;
  const wchar_t* chars =
   (const wchar_t*)(Marshal::StringToHGlobalUni(s)).ToPointer();
  os = chars;
  Marshal::FreeHGlobal(IntPtr((void*)chars));
  return os;
 }

上面的方法申明了一个 wchar_t* 类型的指针,在方法结尾必须释放此指针占用的内存,所以这种形式的转换还是比较麻烦。
有关托管字符串跟C++本机字符串的转换。

托管日期与本机日期数据

在C++中表示日期的结构体是 tm,但是需要注意的是 tm的year部分仅能够表示与1900的差值,所以我们可以写下面2个方法来简单的转换:

 static tm Convert2CppDateTime(DateTime^ dt)
 {
  tm result;
  result.tm_year = dt->Year - 1900;
  result.tm_mon = dt->Month;
  result.tm_wday = dt->Day;
  return result;
 }
 static DateTime^ Covert2NetDateTime(tm cppDate)
 {
  return gcnew DateTime(
   cppDate.tm_year + 1900,
   cppDate.tm_mon,
   cppDate.tm_wday
  );
 }

有了字符串跟日期类型的.NET与C++的相互转换,基本上就能够使用.NET的DTO对象了,因为其它数字类型只要类型兼容,是可以直接使用的,比如int类型。

转换到本机结构体

下面再回来看看 GetUserByID 方法内的对象数据转换部分:

//转换托管类型数据到本机结构体
   Func<String^, Object^>^ entityProp =EntityHelper::EntityCallDelegate(result);
   CppUserInfo user;
   user.ID = (int)entityProp("ID");
   user.Name = (String^)entityProp("Name");// MarshalString((String^)entityProp("Name"));
   user.Birthday = Convert2CppDateTime((DateTime^)entityProp("Birthday"));

现在再看看,采用类似“字典”访问方式的SOD DTO对象,给C++本地结构体转换赋值数据,就很方便了,这也是本篇选择SOD框架作为C++与.NET通信的原因了。

为何不使用序列化的问题

在进行分布式跨平台调用的时候,序列化常常作为一个有效手段被大量使用,但是我们的应用有几个特点:

1,没有分布式,在进程内进行不同语言平台调用;

2,不知道反序列化的类型,因为C++没有直接引用任何.NET框架自身之外的.NET程序集;

3,序列化需要使用反射,而我们本来已经在反射了,会加重负担;

除此之外,使用序列化还会有额外的工作:

4,使用序列化会要求被调用端进行额外的封装;

5,双方需要制定通用的通信协议,并且定制序列化过程,比如常见RPC框架约定的序列化协议

所以,经过仔细考虑后,放弃了使用序列化方式来进行C++与.NET进行进程内通信的想法。

以上所述是小编给大家介绍的在C++中反射调用.NET的方法(二),希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,小编会及时回复大家的!

(0)

相关推荐

  • 在C++中反射调用.NET的方法(一)

    为什么要在C++中调用.NET 一般情况下,我们常常会在.NET程序中调用C/C++的程序,使用P/Invoke方式进行调用,在编写代码代码的时候,首先要导入DLL文件,然后在根据C/C++的头文件编写特殊的C#平台调用代码,例如像下面这个样子: [DllImport("Interop.dll",EntryPoint = "Multiply",CharSet = CharSet.Ansi)] static extern int Multiply(int factor

  • C++ 反射机制详解及实例代码

    C++ 反射机制 一.前言: Java有着一个非常突出的动态相关机制:Reflection,用在Java身上指的是我们可以于运行时加载.探知.使用编译期间完全未知的classes.换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体.或对其fields设值.或唤起其methods.然而C++是不支持反射机制,虽然C++有RTTI(运行时类型识别).但是想要实现C++对象序列化,序列化就是存储到磁盘上,将对象变成一定格式的二

  • 在C++中反射调用.NET的方法(三)

    在.NET与C++之间传输集合数据 上一篇<在C++中反射调用.NET(二)>中,我们尝试了反射调用一个返回DTO对象的.NET方法,今天来看看如何在.NET与C++之间传输集合数据. 使用非泛型集合的委托方法 先看看.NET类中的一个返回列表数据的方法: //返回List或者数组,不影响 C++调用 public List<IUserInfo> GetUsers(string likeName) { List<IUserInfo> users = new List&l

  • 在C++中反射调用.NET的方法(二)

    反射调用返回复杂对象的.NET方法 定义数据接口 上一篇在C++中反射调用.NET(一)中,我们简单的介绍了如何使用C++/CLI并且初步使用了反射调用.NET程序集的简单方法,今天我们看看如何在C++与.NET程序集之间传递复杂对象. 先看看.NET程序集的一个返回对象的方法: public IUserInfo GetUserByID(int userId) { IUserInfo userinfo= EntityBuilder.CreateEntity<IUserInfo>(); user

  • php中动态调用函数的方法

    本文实例讲述了php中动态调用函数的方法.分享给大家供大家参考.具体分析如下: php中你可以动态调用函数,分为以下步骤: 1. 定义一个函数 2. 将函数名(字符串)赋值给一个变量 3. 使用变量名代替函数名动态调用函数 详细代码如下所示: <?php function addition ($a, $b){ echo ($a + $b), "\n"; } $result = "addition"; $result (3,6); ?> 希望本文所述对大家

  • C#中API调用的多种方法

    介绍 API( Application Programming Interface ),我想大家不会陌生,它是我们Windows编程的常客,虽然基于.Net平台的C#有了强大的类库,但是,我们还是不能否认API在Windows编程中的重要性.大多数的编程语言都支持API编程,而.Net平台中的MFC(Microsoft Foundation Class Library)构架本身就封装了大部分的API. 做为程序员,我们需要了解API从字面上了解便是编程接口,因此,做为开发者,需要了解的只是API

  • Node.js中安全调用系统命令的方法(避免注入安全漏洞)

    在这篇文章中,我们将学习正确使用Node.js调用系统命令的方法,以避免常见的命令行注入漏洞. 我们经常使用的调用命令的方法是最简单的child_process.exec.它有很一个简单的使用模式;通过传入一段字符串命令,并把一个错误或命令处理结果回传至回调函数中. 这里是你通过child_process.exec调用系统命令一个非常典型的例子. 复制代码 代码如下: child_process.exec('ls', function (err, data) {     console.log(

  • ubuntu中python调用C/C++方法之动态链接库详解

    安装boost python调用C/C++的方法有很多,本文使用boost.python.考虑到后期有好多在boost上的开发工作,所以boost一并安装了,Boost库分为两个部分来使用,一是直接使用对应的头文件,二是需要编译安装相应的库才可以使用. 具体安装方法可以参考:https://www.jb51.net/article/150380.htm 这里使用: sudo apt-get install libboost-all-dev 服务端 序列化后发送 main.cpp: #includ

  • 解析如何使用反射调用类型成员 方法,字段,属性

    如下所示: 复制代码 代码如下: class Program    {        static void Main(string[] args)        {            Type t = typeof(Test);            object result;            Test tc =new Test();            Console.WriteLine("Invoke a static method");            t.

  • Android利用反射机制调用截屏方法和获取屏幕宽高的方法

    想要在应用中进行截屏,可以直接调用 View 的 getDrawingCache 方法,但是这个方法截图的话是没有状态栏的,想要整屏截图就要自己来实现了. 还有一个方法可以调用系统隐藏的 screenshot 方法,来进行截屏,这种方法截图是整屏的. 通过调用 SurfaceControl.screenshot() / Surface.screenshot() 截屏,在 API Level 大于 17 使用 SurfaceControl ,小于等于 17 使用 Surface,但是 screen

  • C#高效反射调用方法类实例详解

    C#高效反射调用方法类 1.创建一个业务类(HomeService),在类下创建3个方法 2.正常方式调用类的方法 3.反射方式调用类的方法 4.调用代码 5.调用结果 6.Service类方法代码 内容扩展: 1.正常方式调用类的方法 /// <summary> /// 正常调用类的方法(parm1) /// </summary> /// <returns></returns> public string GetNormalMethod_2() { Hom

随机推荐