一种用C++语言动态扩展 C# 程序的方法
新开发者(IT开发者)
本文主要向大家介绍了一种用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#是基于微软公司.NETFramework的面向对象程序设计语言。它在保持了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.1C#基类(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.1C#基类的托管C++适配器(ManagedCsharp)
托管C++类ManagedCsharp包含了CSClass类的所有公有成员函数,并且包含一个void类型的指针p_Csharp,在其构造函数中创建一个CSClass类对象,并且生成一个指向该对象的指针CSClass^p_temp,并且利用.NETFramwork中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::operatorSystem::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.2C++编写的类(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();}
2C#程序动态加载C++类对象
C#程序不能直接通过反射机制动态加载C++编写的类,利用托管C++作为适配器,提供给CSsharp类的派生类CSDerived-
Class类调用, 然后由C#主程序通过反射机制动态加载CS-
DerivedClass类的对象,从而实现对C++类对象的动态创建和调用。
3.1C++类的托管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();} voidManagedUMCpp1::Getdata()
{return p_UMCpp1->Getdata();}
3.2C#派生类(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("IamUMCpp1,Igo"+m_data.ToString () + "step");}
CSDerivedClass2与CSDerivedClass1的差别仅仅在名称上。
//CSDerivedClass2.cs public override void run(){ mumcpp.run();
m_data = mumcpp. p_Csharp.Getdata();
Console.WriteLine("IamUMCpp2,Igo"+m_data.ToString () + "steps");}
3.1C#程序(CSharpProgram)
在C#程序中,生成一个onject对象数组objs,通过反射机制,动态加载CsharpDerived1类和CsharpDerived2类的对象,并通过统一的代码((CSClass)objs[i]).run()调用其run函数,运行结果如图2所示, 其中调用了UMCpp1类run函数(运行了1次
goAhead函数)的CsharpDerived1类对象输出了“IamUMCpp1, I go1step”,调用了UMCpp2类run函数(运行了2次goAhead函数)的CsharpDerived2类对象输出了“IamUMCpp2, Igo2
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+频道!
你的回复
回复请先 登录 , 或 注册相关内容推荐
最新讨论 ( 更多 )
- 【资讯】十多年来,使用过C ++、Ruby、Java语言等多种语言开... (新开发者)
- 学术访谈招募 (废墟上的阅读者)
- 5分钟教会你,QML如何通过WebSocket和C++语言交互? (新开发者)
- 人工智能前沿学生论坛60期| 知识图谱专场 (Jarvis)
- Python 实现曲线点抽稀算法 (新开发者)