跨越C#和C++的单件(SINGLETON)模式

无论是使用C#语言还是C++语言开发出一个单件模式对于大多数人来说都不是太难的一件事。但是使用.Net平台开发项目的时候,一个解决方案中往往会有C#C++两种工程(没办法,谁让C#有那么好的界面功能,以及那么强的XML和数据库操作功能J)。如何使一个类对象实例贯穿在C#C++两种工程中,并且使它在整个过程中有且仅有一个?也就是说,这是不仅仅是一个工程中的单件,而是一个解决方案中的单件。相信这一定是任何一个具有.Net平台开发经验的程序员急切想了解的话题。

 

本文中给出了一个解决方案。这是一个从实际项目开发中总结出来的案例,目的就是要在软件运行过程中保证仅有的一个工程管理类实例存在,而对该管理类实例的使用却要贯穿整个解决方案(C#C++两种工程)。

 

首先让我们来了解一下.Net框架类库提供的两个重要相关类型的使用:

 

GCHandleType 枚举--表示 GCHandle 类可以分配的句柄的类型。

命名空间: System.Runtime.InteropServices
程序集: mscorlib(在 mscorlib.dll 中)

 

成员名称

说明

Normal

此句柄类型表示不透明句柄,这意味着无法通过此句柄解析固定对象的地址。可以使用此类型跟踪对象,并防止它被垃圾回收器回收。当非托管客户端持有对托管对象的唯一引用(从垃圾回收器检测不到该引用)时,此枚举成员很有用。 

Pinned

此句柄类型类似于 Normal,但允许使用固定对象的地址。这将防止垃圾回收器移动对象,因此将降低垃圾回收器的效率。需要使用 Free 方法可尽快释放已分配的句柄。 Use the Free method to free the allocated handle as soon as possible. 

Weak

此句柄类型用于跟踪对象,但允许回收该对象。当回收某个对象时,GCHandle 的内容归零。在终结器运行之前,Weak 引用归零,因此即使终结器使该对象复活,Weak 引用仍然是归零的。 

WeakTrackResurrection

该句柄类型类似于 Weak,但如果对象在终结过程中复活,此句柄不归零。 

 

GCHandle 结构--提供从非托管内存访问托管对象的方法。

命名空间: System.Runtime.InteropServices
程序集: mscorlib(在 mscorlib.dll 中)

 

名称

说明

IsAllocated

获取一个值,该值指示是否分配了句柄。

Target

获取或设置该句柄表示的托管对象。

Alloc

传入一个托管对象,为这个指定的对象分配句柄。缺省为该对象分配GCHandleType. Normal 句柄。

ToIntPtr

返回 GCHandle 对象的内部整数表示形式。

FromIntPtr

传入GCHandle代表的某个托管对象的内部整数表示,返回一个新创建GCHandle 对象。

Free

释放 GCHandle调用方必须确保对于给定的句柄,只调用 Free 一次

   

GCHandle 类与 GCHandleType 枚举结合使用以创建对应于任何托管对象的句柄。此句柄可为四种类型之一:WeakWeakTrackResurrectionNormal Pinned。分配了句柄以后,在非托管客户端保留唯一的引用时,可以使用 GCHandle 防止垃圾回收器回收托管对象。如果没有这样的句柄,则在该对象代表非托管客户端完成工作以前,有可能被垃圾回收器回收。

  
使用 GCHandle 的简单 示例
下面的示例演示 App 类如何使用 GCHandle.Alloc 方法创建托管对象的句柄,以防回收托管对象。
示例使用了一个非托管函数,从 User32.dll 导出的 EnumWindows 。首先我们来了解一下 EnumWindows 的原始函数声明以及该函数的使用:
(This function enumerates all top-level windows on the screen by passing the handle to each window, in turn, to an application-defined callback function. EnumWindows continues until the last top-level window is enumerated or the callback function returns FALSE. )
BOOL EnumWindows( 
  WNDENUMPROC lpEnumFunc, 
  LPARAM lParam );
Parameters
lpEnumFunc
[in] Long pointer to an application-defined callback function.
lParam
[in, out] Specifies an application-defined value to be passed to the callback function.
 
其中
BOOL CALLBACK EnumWindowsProc(
  HWND hwnd, 
  LPARAM lParam );
Parameters
hwnd
[out] Handle to a top-level window.
lParam
[in] Specifies the application-defined value given in EnumWindows
Return Values
TRUE continues enumeration. FALSE stops enumeration.
简单地说 , WNDENUMPROC 是一个回调函数,我们需要定义一个该类型的回调函数,其中是自己想做的操作,当调用 EnumWindows 的时候,每次遇到一个窗口,系统就调用一次你的 WNDENUMPROC ,然后把窗口句柄通过 WNDENUMPROC 的hwnd参数 传给你。而lParam参数则通过 EnumWindows 间接传递给 WNDENUMPROC
示例说明如何将托管对象传递给需要 LPARAM 类型的非托管函数。 LPARAM 类型是指向非托管参数的指针。 在该示例中, LibWrap 类包含 EnumWindows 方法的托管原型。作为其参数,该托管方法用 CallBack 委托替换 WNDENUMPROC 函数指针,并用 IntPtr 指针替换 LPARAM 类型。 App 类使用 GCHandle.Alloc 方法创建托管对象的句柄,从而防止该托管对象被收集。对 EnumWindows 方法的调用传递该委托和托管对象,并将句柄强制转换为 IntPtr 。非托管函数将类型作为回调函数的参数传回调用方。
声明原型
public   delegate   bool  CallBack(  int  handle, IntPtr param );

public   class  LibWrap
{
   
//  传入一个托管对象作为LPARAM参数.(经过IntPtr指针的转换)
   
//  为非托管函数声明一个托管的原型
   [ DllImport(  " user32.dll "  )]
   
public   static   extern   bool  EnumWindows( CallBack cb, IntPtr param );
}

调用函数

public   class  App
{
   
public   static   void  Main()
   {
      TextWriter tw 
=  System.Console.Out;
      GCHandle gch 
=  GCHandle.Alloc( tw );
      CallBack cewp 
=   new  CallBack( CaptureEnumWindowsProc );    
      LibWrap.EnumWindows( cewp, (IntPtr)gch );
      gch.Free();
   }
   
   
private   static   bool  CaptureEnumWindowsProc(  int  handle, IntPtr param )
   {
      GCHandle gch 
=  (GCHandle)param;
      TextWriter tw 
=  (TextWriter)gch.Target;
      tw.WriteLine( handle );
      
return   true ;
   }   
}

 

 

了解了 GCHandle 的作用和使用方法之后,来看看我们实际问题的解决方案。呵呵,以上只是开胃小菜,大餐要真正开始了。
1 C# 构建一个 Project 类的单件模式
建立一个 C# 语言的 Class Libray 工程。在该工程中采用单件模式定义并实现我们所需要的工程管理类 Project.
namespace  ProjectManager
{
    
public   class  Project
    {
        
private   static  Project activeProject  =   null ;
        
private  Project() { }        

        
public   static  Project ActiveProject
        {
            
get
            {
                
if  (activeProject  ==   null )
                    activeProject 
=   new  Project();
                
return  activeProject;
            } 
        }
        
public   bool  DoWorkAboutXML()
        {
            
// ...
             return   true ;
        }
        
public   bool  DoWorkAboutDB()
        {
            
// ...
             return   true ;
        }
    }
}

由于Project的实现采用了单件模式,因此可以保证在任何时刻都只可能有一个Project对象实例存在。通过Project::ActiveProject属性实现该唯一对象的访问。

 

2 .采用托管 C++ 包装一个相应的 AcPpProject 类型

首先创建一个支持CLR的托管工程,声明一个C++类--AcPpProject,在类型定义使用 GCHandle 创建对应于托管对象Project::ActiveProject的句柄。从而可以效防止在该对象代表非托管客户端完成工作以前被垃圾回收器回收。 Class AcPpProject类型中通过一个void *指向GCHandle object.

/// / ProjectManagerMgd.h

class  __declspec(dllexport) AcPpProject
{
public :
    AcPpProject();
    
~ AcPpProject();
    
bool  DoWorkAboutXML();    
    
bool  DoWorkAboutDB();  
protected :
    
void *  m_pProjectGCHandle;
};

//  ProjectManagerMgd.cpp

#include 
" ProjectManagerMgd.h "
using   namespace  ProjectManager;
using   namespace  System::Runtime::InteropServices;

AcPpProject::AcPpProject()
{
    GCHandle gch 
=  GCHandle::Alloc(Project::ActiveProject);

    m_pProjectGCHandle 
=  GCHandle::ToIntPtr(gch).ToPointer();
}

AcPpProject::
~ AcPpProject ()
{
    GCHandle gch 
=  GCHandle::FromIntPtr(System::IntPtr(m_pProjectGCHandle)); 
    gch.Free();
    m_pProjectGCHandle 
=   0 ;
}

对于Project中的每一个实例方法,在AcPpProject都有相应的对应方法定义,并通过将m_pProjectGCHandle还原为原来的Project对象来具体实现调用。因此AcPpProject中的对应方法函数都是对Project中的方法的包装实现。  

inline Project ^  GETPROJECT( void *  pProjectGCHandle)
{
    Project
^  pProject  =  nullptr;
    
if  (pProjectGCHandle  !=   0 )
    {
        GCHandle gch 
=  GCHandle::FromIntPtr(System::IntPtr(pProjectGCHandle));
        pProject 
=  static_cast < Project ^> (gch.Target);
    }
    
return  pProject;
}

bool  AcPpProject::DoWorkAboutXML()
{
    Project
^  pProject  =  GETPROJECT(m_pProjectGCHandle);
    
if  (pProject  ==  nullptr)
    {
        
return   false ;
    }
    
return  pProject -> DoWorkAboutXML();    
}

bool  AcPpProject::DoWorkAboutDB()
{
    Project
^  pProject  =  GETPROJECT(m_pProjectGCHandle);
    
if  (pProject  ==  nullptr)
    {
        
return   false ;
    }
    
return  pProject -> DoWorkAboutDB();
}

 

 

因为我们在 AcPpProject 的声明中没有出现任何托管相关的内容(使用 void 指针巧妙替换了 GCHandle 对象),并使用 __declspec(dllexport) AcPpProject export 出去,从而保证 ProjectManager.h 可以被用于任何 native C++ (no /cli) 工程。这样一来,整个过程中将统一使用唯一的一个托管 Project 类对象。实现了跨越 C# C++ 两种语言的单件模式。
Note:
 
A GCHande is a managed object that allows native code to access a managed variable. Managed variables may be moved around in physical memory by the managed runtime memory manager and therefore native code cannot have a direct pointer to them. That is where GCHandle comes in. The managed memory manager does not move GCHandle around so native code can have a reliable pointer to it. If the managed memory manager moves the managed variable, it updates any GCHandles that point to the variable that was moved.  
 
 
Let me try to clarify relationships:
 
 
 
Native pointer               GCHandle               Managed Variable
 
X                                 Y                           Z   (can move around in memory)
 
X is a native pointer to Y.
 
Y is created and not normally exist, once created, the managed run-time keeps it up to date.
 
Y.Target is Z
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
单例模式Singleton Pattern)是一种创建型模式,它保证一个类只有一个实例,并提供一个全局访问点。单例模式通常用于控制某些资源的访问权限,或者在整个系统中只需要一个共享资源的情况下使用。 在单例模式中,类的构造函数必须私有化,这样外部就不能直接实例化该类。同时,类中需要定义一个静态方法,用于获取该类唯一的实例。在第一次调用该方法时,会创建一个实例,并将其保存下来。以后每次调用该方法时,都会返回同一个实例。 例如,下面的代码演示了如何在C++中实现单例模式: ```c++ class Singleton { public: static Singleton& getInstance() { static Singleton instance; // 延迟初始化,保证线程安全 return instance; } private: Singleton() {} // 将构造函数私有化,防止外部实例化 Singleton(const Singleton&) = delete; // 禁止拷贝构造函数 Singleton& operator=(const Singleton&) = delete; // 禁止赋值运算符 }; int main() { Singleton& s1 = Singleton::getInstance(); Singleton& s2 = Singleton::getInstance(); std::cout << std::boolalpha << (&s1 == &s2) << std::endl; // 输出:true return 0; } ``` 在这个例子中,我们定义了一个名为Singleton的类,并将其构造函数私有化,防止外部实例化。同时,我们定义了一个静态方法getInstance,用于获取该类唯一的实例。在getInstance方法中,我们使用了静态局部变量的方式来延迟初始化,保证线程安全。最后,在main函数中,我们多次调用getInstance方法,每次调用都会返回同一个实例。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值