在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(, , ); 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++本机字符串的转换,可以参考下面2篇文章:
http://bbs.csdn.net/topics/280024331
http://blog.csdn.net/windren06/article/details/7839985
托管日期与本机日期数据
在C++中表示日期的结构体是 tm,但是需要注意的是 tm的year部分仅能够表示与1900的差值,所以我们可以写下面2个方法来简单的转换:
static tm Convert2CppDateTime(DateTime^ dt)
{
tm result;
result.tm_year = dt->Year - ;
result.tm_mon = dt->Month;
result.tm_wday = dt->Day;
return result;
} static DateTime^ Covert2NetDateTime(tm cppDate)
{
return gcnew DateTime(
cppDate.tm_year + ,
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如何传递集合对象的问题。
(未完待续)
在C++中反射调用.NET(二)的更多相关文章
- 在C++中反射调用.NET(三)
在.NET与C++之间传输集合数据 上一篇<在C++中反射调用.NET(二)>中,我们尝试了反射调用一个返回DTO对象的.NET方法,今天来看看如何在.NET与C++之间传输集合数据. 使 ...
- 在C++中反射调用.NET(一)
为什么要在C++中调用.NET 一般情况下,我们常常会在.NET程序中调用C/C++的程序,使用P/Invoke方式进行调用,在编写代码代码的时候,首先要导入DLL文件,然后在根据C/C++的头文件编 ...
- [LinqPad妙用]-在Net MVC中反射调用LinqPad中的Dump函数
LinqPad有个非常强大的Dump函数.这篇讲解一下如何将Dump函数应用在.Net MVC Web开发中. 先看效果: 一.用.Net Reflector反编译LinqPad.exe,找出Dump ...
- Java实现Qt的SIGNAL-SLOT机制(保存到Map中,从而将它们关联起来,收到信号进行解析,最后反射调用)
SIGNAL-SLOT是Qt的一大特色,使用起来十分方便.在传统的AWT和Swing编程中,我们都是为要在 监听的对象上添加Listener监听器.被监听对象中保存有Listener的列表,当相关事件 ...
- 【SSH】——Struts2中的动态方法调用(二)
当action中的方法有很多时,那应该怎么调用呢?上次我们提到的UserAction类中只有一个execute方法,如果我们需要增加用户的增删改查方法,如下: public class UserAct ...
- STUN/TURN/ICE协议在P2P SIP中的应用(二)
1 说明 2 打洞和穿越的概念... 1 3 P2P中的打洞和穿越... 2 4 使用STUN系列 协议穿越的特点... 2 5 STUN/ ...
- 通过Java反射调用方法
这是个测试用的例子,通过反射调用对象的方法. TestRef.java import java.lang.reflect.Method; import java.lang.reflect.In ...
- JAVA中反射机制一
反射一 基本概念 一.反射机制的基本概念 什么是反射?反射是指在运行状态中,对于任意一个类,都可以获取到这个类的所有属性和方法:对于任意一个对象,都能够调用这个对象的任意方法和属性:这种动态获取信息及 ...
- java中反射的使用
结合demo理解反射: import java.lang.reflect.*; /** * 反射使用 **/ public class ReflectDemo{ public static void ...
随机推荐
- 一、MongoDB安装及启动
1 安装 在官网http://www.mongodb.org/downloads下载对应版本,并安装.安装在D:\MongoDB下. 2 设置 1) 新建文件夹,用于存放db数据 D:\MongoDB ...
- shell之crontab
一 crontab高级用法 Crontab 主要是在做排程, 通常一般寫法大概都是如下: 0 0 * * * /usr/local/www/awstats/cgi-bin/awstats.sh 這種 ...
- shell字符串操作之cut---实现字符串截取
shell中(字符串截取) cut是以每一行为一个处理对象的,这种机制和sed是一样的.(关于sed的入门文章将在近期发布) 2 cut一般以什么为依据呢? 也就是说,我怎么告诉cut我想定位到的剪切 ...
- 流水线技术原理和Verilog HDL实现(转)
源:流水线技术原理和Verilog HDL实现 所谓流水线处理,如同生产装配线一样,将操作执行工作量分成若干个时间上均衡的操作段,从流水线的起点连续地输入,流水线的各操作段以重叠方式执行.这使得操作执 ...
- 【iScroll源码学习01】准备阶段 - 叶小钗
[iScroll源码学习01]准备阶段 - 叶小钗 时间 2013-12-29 18:41:00 博客园-原创精华区 原文 http://www.cnblogs.com/yexiaochai/p/3 ...
- EF LINQ根据某个字段去除重复行
1.ydc.GameScore.OrderByDescending(o => o.Score).GroupBy(ic => ic.UserPhone).Select(g => g.F ...
- 苹果App Store开发者帐户从申请,验证,到发布应用(4)
苹果App Store应用内支付(In-App Purchase) IAP简介 IAP的全称是In-App Purchase,应用内付费.这种业务模式允许用户免费下载试用,对应用内提供的商品选择消费, ...
- STM32F412应用开发笔记之八:迪文串口屏显示驱动
迪文的显示屏使用起来比较方便,其使用串口通讯,即可支持RS232,又可以支持TTL电平.在NUCLEO-F412ZG实验板上,USART2已经引到了CN9上,我们就利用USART2来实现与迪文串口屏的 ...
- 【转】23种设计模式UML图
原文:http://blog.csdn.net/bwwlpnn/article/details/7421628
- angularjs ajax传参
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <script sr ...