对上篇中提到的问题,还有一种解决办法。
在C#中,除了public、protected和private访问修饰符之外,还有一种,叫internal,并且还能和protected组合成protected internal。
声明的可访问性 |
含义 |
---|---|
public |
访问不受限制。 |
protected |
访问仅限于包含类或从包含类派生的类型。 |
internal |
访问仅限于当前程序集。 |
protected internal |
访问仅限于从包含类派生的当前程序集或类型。 |
private |
访问仅限于包含类型。 |
internal很有意思。假设某个变量或者类型(称之为x吧)被internal修饰,那么,只有和x同属一个程序集的代码才能访问x。下面是来自MSDN的示例:
该示例包含两个文件:Assembly1.cs 和 Assembly2.cs。第一个文件包含内部基类BaseClass。在第二个文件中,实例化 BaseClass 的尝试将产生错误。
// Assembly1.cs
// Compile with: /target:library
internal class BaseClass
{
public static int intM = 0;
}
// Assembly2.cs// Compile with: /reference:Assembly1.dllclass TestAccess { static void Main() { BaseClass myBase = new BaseClass(); // CS0122 }}
internal就是我们所需要的东西:
internal class CHasAAnalysisSystem { ... };
class CAnalysisSample : public CHasAAnalysisSystem
{
protected:
CAnalysisSample(CAnalysisSystem* pSystem):CHasAAnalysisSystem(pSystem){}
};
可惜C++中没有internal(托管C++有没有我不知道,没接触过),以后会不会有不敢保证,至少目前还没有。
值得庆幸的是,我们可以模拟它,虽然在一定程度上有点问题。
C++中有嵌套类(顺便提一下,还有局部类和匿名类。局部类如:
if(...) {
class className{}; // 处于块中
className o;
}
局部类没有linkage属性,且不能作为模板参数。
匿名类如:
class {
class {} m_o; //和嵌套类类似,但没有名字,不过可以直接生成实例;struct、union、enum的这种用法比较常见
};
)。
我们可以为嵌套类设置访问修饰符:
class OuterClass
{
public:// 或者protected、private
class InnerClass
{
};
};
那么,如何模拟C#中的internal呢?
其实很简单(针对我们的具体问题),我们只需要将CHasAAnalysisSystem设置为private即可。当然,为了能够访问CHasAAnalysisSystem,CAnalysisSample等也需要成为内部类。我们可以给外部类起名为Analysis,则CHasAAnalysisSystem和CAnalysisSample可以更名为CHasASystem和CSample。代码如下:
class Analysis
{
private:
class CHasASystem
{
};
public:
class CSample : public CHasASystem
{
protected:
CSample(CSystem* pSystem):CHasASystem(pSystem){}
};
};
如此,CSample中便无需写上一堆using,而又能达到我们的目的。
Analysis::CSample* pSample = ...;
Analysis::CHasASystem* pHasASystem = pSample; // 错误,无法访问Analysis::CHasASystem
delete pHasASystem; // 如果CSample和CHasASystem构成is-a关系,则应当可以通过基类的指针正确地删除派生类对象。
// 而事实上它们不是is-a关系,因此我们想在概念加以区分,在使用上加以禁止。
用内部类模拟internal来解决我们的问题,有两个小瑕疵:
首先,对于用户,必须使用完整的限定名:Analysis::CSample、Analysis::CMethod...如果是命名空间,则用户可以使用using Analysis;CSample、CMethod...
其次,用户阅读Analysis的代码时需要停顿:CHasASystem是private的,而CSample却public继承了它,说明CSample想继承CHasASystem的接口和实现,缺又不想暴露CHasASystem。
还有更好的办法吗?