一种用C++语言动态扩展 C# 程序的方法
小标 2018-07-25 来源 : 阅读 1309 评论 0

摘要:本文主要向大家介绍了一种用C++语言动态扩展 C# 程序的方法,通过具体的内容向大家展示,希望对大家学习C++语言有所帮助。

本文主要向大家介绍了一种用C++语言动态扩展 C# 程序的方法,通过具体的内容向大家展示,希望对大家学习C++语言有所帮助。

    提出一种用非托管 C++( 以下简称 C++) 动态扩展 C# 程序的方法。 利用托管 C++ 作为适配器, 由 C++ 类继承 C# 基类, 并且获取 C# 程序提供的服务; 将 C++ 类利用托管 C++ 作为适配器, 通过 C# 基类的派生类提供给 C# 程序动态加载。 实例表明该方法能够使 C++ 编写的类继承 C#程序中的类, 获取 C# 程序提供的服务; 并且使 C# 程序能够动态创建并调用 C++ 类对象。 该方法能够为 C++ 源代码的重用、C++ 源代码与.NET 平台语言的混合编程等提供解决方案。

1 引言

C++是国内外广泛使用的现代计算机语言, 它既支持面向过程的程序设计,也支持基于对象和面向对象的程序设计。经过多年的积累, 已经形成了大量C++编写的源代码或开源类库且应用广泛。

C# 是基于微软公司.NET Framework 的面向对象程序设计语言。它在保持了 C++中大部分语法的同时,添加了大量的高效代码和完全面向对象特性,以及更高的可靠性和安全性。它不仅能用于 Web 服务程序的开发,并且还能支持系统级程序开发和界面开发。

为了提高开发效率,综合利用两种语言的优势,在一些应用软件中,需要用 C++动态扩展 C# 程序,主要包括 C++类获得 C# 程序所提供的服务,以及 C#程序动态创建并调用 C++类对象两个方面。 目前用 C++动态扩展 C# 程序主要有基于源代码转换的方法、基于动态链接库和基于 COM 组件等方法,其中基于源代码转换的方法目前还不完善, 对于 C++的指针等特殊语法的转换还不能满足实际需求。 而基于动态链接库和基于 COM 组件的方法虽然能完成一定的互相调用,  但是这些方法都没有很好的支持 C++类继承 C# 类,以及 C# 程序动态创建并调用 C++ 类对象等功能。

提出了一种 C++类与 C# 程序的动态链接方法, 如图 1 所示,C# 主程序 (CSharpProgram)  的应用框架建立在 C# 基类

(CSClass(Base))之上, 利用托管 C++(ManagedCpp)作为适配器提供给非托管(Unmanaged)C++编写的类(UMCppClass)继承;为了动态创建和调用C# 基类派生的非托管 C++类对象,编写该基类的派生类(CSClass(Derived)),利用托管 C++作为适配器将 C++类提供给该 C# 派生类调用,C# 程序通过反射机制, 动态加载 C# 派生类的对象,从而实现对 C++类对象的动态创建和调用。

 

 

2 C++类继承 C# 程序中的类

非托管 C++类不能直接继承 C# 类,利用托管 C++作为适配器,将 C# 类提供给非托管 C++继承,并且获取 C# 程序提供的服务。

2.1 C# 基类(CSClass)

以一个普通的 C# 基类为例,  该 C# 基类 CSClass 具有一个

int 型数据成员 m_data(初值为 0)及其读取函数 Getdata();一个普通成员函数 goAhead(),在该函数中 m_data 递增;一个虚函数 run

(),提供给派生类覆盖,以实现不同方式的 run()。 CSClass 类的关键代码段如下:

//CSClass.cs

public class CSClass{

public int m_data;//数据成员

public CSClass() {m_data = 0;}

public int Getdata(){return m_data;} public void goAhead()//普通成员函数

 

{m_data ++;}

public virtual void run(){}//虚函数

……//其他函数或数据成员}

2.1 C# 基类的托管 C++适配器(ManagedCsharp)

托管 C++类 ManagedCsharp 包含了 CSClass 类的所有公有成员函数,并且包含一个 void 类型的指针 p_Csharp,在其构造函数中创建一个CSClass 类对象, 并且生成一个指向该对象的指针 CSClass^ p_temp, 并且利用.NET Framwork 中 System. Run-

time.InteropServices 命名空间中的 GCHandle 结构完成 Void 类型指针与指向 CSClass 对象指针之间的转换,通过指向 CSClass 对象的指针调用CSClass 的成员函数。ManagedCsharp 的关键代码段如下:

//ManagedCsharp.h class ManagedCsharp{ public:

ManagedCsharp ();

~ ManagedCsharp (); int Getdata();

virtual void run(); void goAhead(); void* p_Csharp;};

//ManagedCsharp.cpp

using namespace System::Runtime::InteropServices;

// 将 void 指针转换为指向 CSClass 类对象的指针inline CSClass^ void2CSClass(void *pHandle){……} ManagedCsharp::ManagedCsharp(){

p_Csharp = NULL;

CSClass^ p_temp = gcnew CSClass(); GCHandle handle = GCHandle::Alloc(p_temp);

p_Csharp = (GCHandle::operator System::IntPtr (handle)). To- Pointer(); }

ManagedCsharp::~ManagedCsharp(){ if (p_Csharp == NULL) return;

GCHandle handle = GCHandle::operator GCHandle (Sys- tem :: IntPtr(p_Csharp)) ;

handle.Free(); p_Csharp = NULL; }

void ManagedCsharp::run(){

CSClass^ p_temp = void2CSClass(p_Csharp); p_temp->run();}

void ManagedCsharp::goAhead(){

CSClass^ p_temp = void2CSClass(p_Csharp); p_temp->goAhead();}

int ManagedCsharp::Getdata(){

CSClass^ p_temp = void2CSClass(p_Csharp); return p_temp->Getdata();}

2.2 C++编写的类(UMCppClass)

用 C++ 编写的类继承 ManagedCsharp 类,  以两个不同的

C++类 UMCppClass1 和 UMCppClass2 为例,这两个类均为 Man-

agedCsharp 的派生类,它们都覆盖了基类中的 run 虚函数,但是提供了不同的实现, 其中 UMCPPClass1 中的 run 函数运行 1 次goAhead(), 而UMCPPClass2 中的 run 函数运行 2 次 goAhead()。

这两个类的关键源代码如下:

//UMCppClass1.h

class UMCppClass1 :public ManagedCsharp{ public:

UMCppClass1 (void);

~UMCppClass1 (void); virtual void run();};

//UMCppCLass1.cpp

//UMCPPClass1 中的 run()运行 1 次 goAhead() void UMCppCLass1::run(){goAhead();}

//UMCppCLass2.cpp

//UMCPPClass1 中的 run()运行 2 次 goAhead() void UMCppCLass2::run(){goAhead();goAhead();}

2 C# 程序动态加载 C++类对象

C# 程序不能直接通过反射机制动态加载 C++编写的类,利用托管 C++作为适配器, 提供给 CSsharp 类的派生类 CSDerived-

Class 类调用,  然后由 C# 主程序通过反射机制动态加载 CS-

DerivedClass 类的对象,从而实现对 C++类对象的动态创建和调用。

3.1 C++类的托管 C++适配器(ManagedUMCpp)

托管 C++类 ManagedUMCpp 类包含了 UMCppClass 类的所有公有成员函数, 并且包含一个指向 UMCppClass 类对象的指针 p_UMCpp,  还 包 含了 一 个 指 向 CSClass 类 对 象 的 指 针

p_Csharp,   用 于 存 储 UMCppCLass  类 对 象 中 的 数 据 成 员

p_Csharp, 为 C# 派生类 CSDerivedClass 中数据成员的赋值做准备。 由于 ManagedUMCpp1 类与 ManagedUMCpp2 类的源代码仅仅在类名上不同,以下仅列举 ManagedUMCpp1 类的关键代码段:

//ManagedUMCpp1.h

public ref class ManagedUMCpp1{ public:

ManagedUMCpp1 ();

~ ManagedUMCpp1 (); void virtual run();

void goAhead(); int Getdata();

UMCppCLass1 * p_UMCpp; CSClass ^ p_Csharp;};

//ManagedUMCpp1.cpp ManagedUMCpp1:: ManagedUMCpp1(){

p_UMCpp = new UMCppClass1();

p_Csharp = GetImpObj(p_UMCpp ->p_Csharp);} ManagedUMCpp1::~ManagedUMCpp1(){delete p_UMCpp ;} void ManagedUMCpp1::run(){p_ UMCpp ->run();}

void ManagedUMCpp1::goAhead(){p_UMCpp1-> goAhead();} void ManagedUMCpp1::Getdata()

{return p_UMCpp1->Getdata() ;}

3.2 C# 派生类(CSDerivedClass)

C# 派生 类 CSDerivedClass 继 承 CSClass 类 。 包 含 一 个

ManagedUMCpp 类的对象,由于 UMCppClass 类的函数只能直接改变 CSClass 类对象的数据成员 mumcpp,  所以在 CSDerived-

Class 中通过 mumcpp 的 p_Csharp 指针获取 UMCppClass 中生成的 CSClass 类对象的数据成员值,保持 CSDerivedClass 对象数

据成员与 CSsharp 数据成员值的一致。 为了演示不同 C++代码实现的不同的虚函数 run(), 在 CSDerivedClass 的 run 函数中输出了其数据成员m_data 的值。 该类的关键代码段如下:

//CSDerivedClass1.cs

public class CSDerivedClass1: CSClass{ public ManagedUMCpp1 mumcpp; public CSDerivedClass1()

{ mumcpp = new ManagedUMCpp1();} public override void run(){

mumcpp.run();

m_data = mumcpp. p_Csharp.Getdata();

Console.WriteLine("I am UMCpp1, I go " + m_data. ToString () + " step");}

CSDerivedClass2 与 CSDerivedClass1 的差别仅仅在名称上。

//CSDerivedClass2.cs public override void run(){ mumcpp.run();

m_data = mumcpp. p_Csharp.Getdata();

Console.WriteLine("I am UMCpp2, I go " + m_data. ToString () + " steps");}

3.1 C# 程序(CSharpProgram)

在 C# 程序中, 生成一个 onject 对象数组 objs, 通过反射机制, 动态加载 CsharpDerived1 类和 CsharpDerived2 类的对象,并通过统一的代码((CSClass)objs[i]).run() 调用其 run 函数, 运行结果如图 2 所示,  其中调用了 UMCpp1 类 run 函数 (运行了 1 次

goAhead 函数)的 CsharpDerived1 类对象输出了“I am UMCpp1, I go 1 step”,调用了 UMCpp2 类 run 函数(运行了 2 次 goAhead 函数) 的CsharpDerived2 类对象输出了 “I am UMCpp2, I go 2

steps”而可以发现,在不修改 C# 程序调用 run 函数的代码情况下,完成了动态加载不同 C++类的不同 run 函数实现,通过 C++ 动态扩展了 C# 程序。以下是 C# 程序的关键代码段:

//CSharpProgram.cs

static void Main(string[] args){ object[] objs = new object[2];

objs [0] = Assembly.Load ("CsharpDerived1"). CreateInstance ("CsharpDerived1.CSDerivedClass1");

objs [1] = Assembly.Load ("CsharpDerived2"). CreateInstance ("CsharpDerived2.CSDerivedClass2");

for(int i = 0; i<2; i++)

{((CSClass)objs[i]).run();}}

 

 

4 实例分析

 

最后将该方法应用于基于.NET 的 Robocode 教学系统中,

 

Robocode 是美国 IBM 公司开发的机器人 (其图形为坦克的形状) 战斗仿真引擎。 不同用户利用 Java 语言对机器人进行编程, 给机器人设计赋予不同“智能”指挥它的行动,仿真引擎图形化地显示战斗的过程和结果。 可以使用户在娱乐中学习 java 编程。作者编写了一个.NET 平台下的 Robocode 教学系统,用于 C# 语言以及.NET 平台下的其他语言教学。

 

将该方法应用于该教学系统中, 可以将其扩展到非托管

 

C++语言的教学中。 C# 主程序(CSharpMain)的应用框架建立在

 

C# 基类(Tanks)上,利用托管 C++(ManagedCpp)作为适配器提供给用户编写的 C++类(MyTank)继承;利用托管 C++作为适配器将 MyTank 类提供给 Tanks 的派生类 VirtualTanks 调用,C# 主程序通过反射机制,动态加载 C# 派生类的对象,从而实现对 C++ 类对象的动态创建和调用。

 

图 3(a)为两个不同用户 C++ 类 MyTank1 和 MyTank2 的源代码片段, 它们在给出了基类中 run()、onHitWall()、OnBullethit-

 

Tank() 等虚函数的不同实现, 分别表示了坦克运行时的基本动作,坦克在碰到墙后的动作以及坦克被子弹击中后的动作。 图 3

 

(b) 显示了主系统动态载入了两个“ 坦克”MyTank1 和 MyTank2

 

后的战场界面,两个坦克分别有不同的运行策略,实现了 C++对

 

C# 程序的动态扩展。

 

 

 

 

 

 

5 结论

 

通在一些应用软件中,需要利用非托管 C++动态扩展 C# 程序,而.NET Framework 没有提供 C# 程序与 C++之间直接的动态链接机制(包括类的继承、动态加载类对象等)。 提出了一种用非托管 C++动态扩展 C# 程序的方法, 利用托管 C++作为适配器, 由 C++类继承 C# 基类,并且覆盖(override)基类中相应的虚函数, 获取 C# 程序提供的服务; 将 C++类利用托管 C++作为适配器, 通过 C# 基类的派生类提供给 C# 程序动态加载,从而使 C++编写的类继承 C# 程序中的类,并且使 C# 程序能够动态创建并调用 C++类的对象, 将该方法应用于基于.NET 的 Robocode 教学系统,实例表明提出的方法能够使 C++编写的类继承 C# 程序中的类,获取 C# 程序提供的服务;并且使 C# 程序能够动态创建并调用 C++类对象。能够实现动态载入不同 C++用户代码实现 C# 基类中相应虚函数的功能,为 C++源代码的重用、C++与.NET 平台语言的混合编程等提供有效的解决方案。

 

本文创新点: 提出一种用 C++动态扩展 C# 程序的方法。利用托管 C++作为适配器,由 C++类继承 C# 基类,并通过 C# 基类的派生类提供给 C# 程序动态加载。

 

 

如图 3 所示, 采用启发式选择策略能够获得更佳的系统传输延迟。并且,与随机选择策略相比,系统传输延迟由 2.5 秒降低至 1.7 秒,降低幅度大约为30%。

另一方面,从图 3 中可以看出,采用随机选择策略,当系统传输延迟逐渐趋于稳定后(250 秒后),随着节点的加入,系统传输延迟逐渐升高,当节点停止加入后,系统传输延迟则基本保持不变; 而采用基于服务能力的启发式选择策略,在节点加入后期(1500 秒后),系统传输延迟不仅没有升高,而且还呈现下降的趋势。 并且,当节点停止加入后,系统传输延迟仍然趋于下降,这是因为: 在运行过程中,  节点将根据自身的服务能力对其在系统中的位置进行自适应调整,服务能力高的节点将逐渐向数据源靠近,以降低层状结构的高度,从而获得更优的系统传输延迟。而调整过程是持续的,  直至节点的合作节点的服务能力均不低于自身的服务能力。

6 总结

本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标编程语言C/C+频道!


本文由 @小标 发布于职坐标。未经许可,禁止转载。
喜欢 | 1 不喜欢 | 0
看完这篇文章有何感觉?已经有1人表态,100%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程