Effecitve C# 原则46:最小化与其它非托管代码的交互
2009-02-19 08:17:24 来源:WEB开发网最后一种选择就是在Microsoft C++编译器上使用/CLR开关来混合托管的非托管代码。如果你编译你所有的本地代码使用/CLR,你就创建了一个基于MSIL的库,该库使用本地堆来存储所有的数据。这就是说,这样的C++库不能直接被C#调用。你必须在你所熟悉的代码上创建一个托管的C++库,用于在托管和非托管类型之间创建一个桥梁,提供在托管和非托管的堆之间的数据集群支持。这样的C++库包含托管类,这些数据成员是基于托管堆的。这些类同样包含对本地对象的引用:
// Declare the managed class:
public __gc class ManagedWrapper : public IDisposable
{
private:
NativeType* _pMyClass;
public:
ManagedWrapper( ) :
_pMyClass( new NativeType( ) )
{
}
// Dispose:
virtual void Dispose( )
{
delete _pMyClass;
_pMyClass = NULL;
GC::SuppressFinalize( this );
}
~ManagedWrapper( )
{
delete _pMyClass;
}
// example property:
__property System::String* get_Name( )
{
return _pMyClass->Name( );
}
__property void set_Name( System::String* value )
{
char* tmp = new char [ value->Length + 1 ];
for (int i = 0 ; i < value->Length; i++ )
tmp[ i ] = ( char )value->Chars[ i ];
tmp[ i ] = 0;
_pMyClass->Name( tmp );
delete [] tmp;
}
// example method:
void DoStuff( )
{
_pMyClass->DoStuff( );
}
// other methods elided...
}
再说一次,这并不是一个像以前使用过的高产的程序开发工具。这只是代码重复,而且整个目的都要在托管和非托管数据之间进行集群数据和thunking。优势是,你可以从你的本地代码中暴露出来的方法和属性上完成控制。而劣势是你必须手写一部份.Net代码以及一部份C++代码。在这两种代码之间转换很容易让你发生错误。你还不能忘记删除非托管对象。托管对象不是你 要负责的。这会让你的开发进度降下来,因为要不断的检测这是否正确。
使用 /CLR 开关听上去像是一个神话,但对于所有的交互来说,这并不是一个神弹(magic bullet)。C++里的模板和异常处理与C#是大不相同的。写的很好很高效的C++并不一写能转化成最好的MSIL结构。更重要是,编译C++代码时的 /CLR开关并不做确认。正如我前面所说的,这样的代码是使用本地堆:它访问本地内存。而CLR并不能验证这些代码是安全的。调用这些代码的程序必须确保有安全许可来访问不安全代码。虽然如此,/CLR策略还是最好的一个方法,在.Net中利用已经存在的C++代码(不是COM对象)。你的程序不会招致thunking开销,而为你的C++库现在并不在MSIL中,MSIL不是本地的CPU指令。
交互操作是件头疼的事。在你使用交互之间,认真的考虑写本地应用程序。这经常是简单而且很快的。不幸的是,对于很多的开发人员来说,交互是必须的。如果你有已经存在的用其它语言写的COM对象,使用COM交互。如果你有已经存在的C++代码,使用 /CLR 开关并托管C++来提供最好的策略来方法已经存在的本地代码。选择最省时间的一个策略,这可能就是“just thro it out”策略。
更多精彩
赞助商链接