您什么时候应该使用私人/内部课程?


21

为了澄清,我要问的是

public class A{
    private/*or public*/ B b;
}

public class A{
    private/*or public*/ class B{
        ....
    }
}

我绝对可以考虑使用其中一个的某些原因,但我真正想看到的是令人信服的示例,这些示例表明了利弊不只是学术上的。


2
答案将取决于语言和核心库提供的功能。假设使用C#语言,请尝试通过StyleCop运行它们。我很确定您会收到有关将此类分离到自己的文件中的警告。这意味着MSFT的足够多的人认为您不需要使用任何一个示例。
工作

如果学术例子不够好,那将是一个令人信服的例子?

@Job StyleCop仅在从外部可见内部类的情况下才会发出警告。如果它是私有的,那完全可以,实际上有很多用途。
julealgon 2014年

Answers:


20

当一个类是另一个类的内部实现细节而不是其外部接口的一部分时,通常使用它们。我主要将它们视为仅数据类,这些类在没有针对c样式结构的单独语法的语言中使内部数据结构更整洁。有时它们对于事件处理程序或工作线程之类的高度定制的,从一种方法派生的对象也很有用。


2
这就是我使用它们的方式。如果从功能的角度来看,一个类只是另一个类的私有实现细节,则应该以这种方式声明它,就像私有方法或字段那样。没有理由用永远不会在其他地方使用的垃圾来污染名称空间。
2011年

9

假设您正在构建树,列表,图等。为什么要向外部世界公开节点或单元的内部细节?

使用图形或列表的任何人都应该仅依赖于其接口,而不是其实现,因为您可能希望将来有一天将其更改(例如,从基于数组的实现更改为基于指针的实现),而使用您的客户端数据结构(每个)都必须修改其代码,以使其适合新的实现。

相反,将节点或单元的实现封装在私有内部类中,可以让您自由地在需要时随时修改实现,而不会迫使客户端相应地调整其代码,只要数据结构的接口保持不变即可。无动于衷。

隐藏数据结构的实现细节也会带来安全优势,因为如果您要分发类,则只会使接口文件与已编译的实现文件一起可用,而没人会知道您是否在实际使用数组或指针的实现,从而保护您的应用程序免受某种形式的利用或至少由于不当使用或检查您的代码而导致的知识。除了实际问题之外,请不要低估这种情况下这是一个非常优雅的解决方案。


5

我认为将内部定义的类用于在类中短暂使用以允许特定任务的类是公平的。

例如,如果您需要绑定由两个不相关的类组成的数据列表:

public class UiLayer
{
    public BindLists(List<A> as, List<B> bs)
    {
        var list = as.ZipWith(bs, (x, y) => new LayerListItem { AnA = x, AB = y});
        // do stuff with your new list.
    }

    private class LayerListItem
    {
        public A AnA;
        public B AB;
    }
}

如果您的内部类被另一个类使用,则应将其分开。如果内部类包含任何逻辑,则应将其分开。

基本上,我认为它们非常适合在数据对象中插入漏洞,但是如果它们实际上必须包含逻辑,则很难维护它们,因为如果需要更改它们,则必须知道在哪里寻找它们。


2
假设使用C#,您不能在这里只使用元组吗?
工作

3
@Job当然可以,但是有时tuple.ItemX会使代码不清楚。
Lasse Espeholt 2011年

2
@Job是的,lasseespeholt正确地做到了这一点。我宁愿看到一个{字符串Title; 字符串说明;},而不是Tuple <string,string>。
Ed James

到那时,将该类放入自己的文件中没有意义吗?
工作

不,不是,如果您只是真正地使用它来简单地将一些数据绑定在一起,以完成一项不超出父类权限的任务。至少,我不认为!
Ed James

4
  • 某些语言的内部类(例如Java)与包含类的对象相关联,并且可能使用其成员而没有限定它们的资格。如果可用,它比使用其他语言工具重新实现它们更清晰。

  • 其他语言(例如C ++)的嵌套类没有这种联系。您只需使用该类提供的作用域和可访问性控件。


3
实际上,Java 兼而有之
约阿希姆·绍尔

3

我避免使用Java中的内部类,因为在存在内部类的情况下热交换不起作用。我讨厌这个,因为我真的不愿意出于这样的考虑而折衷代码,但是那些Tomcat重新启动加起来了。


该问题记录在某处吗?
t

Downvoter:我的主张不正确吗?
凯文·克莱恩

1
我之所以投票,是因为您的回答缺少细节,而不是因为它不正确。缺乏细节使您的主张难以验证。如果您在此问题上添加更多内容,我可能会回复投票,因为您的信息看起来很有趣并且可能很有用
gnat 2012年

1
@gnat:我做了一些研究,尽管这似乎是一个众所周知的事实,但是我没有找到关于Java HotSwap局限性的明确参考。
凯文·克莱恩

我们不是在怀疑论者上。SE :)只是一些参考就可以了,它不一定是确定的
gnat 2012年

2

私有静态内部类的用途之一是备注模式。您将所有需要记住的数据放入私有类,并从某些函数作为对象返回。外面的人都无法(无需进行反射,反序列化,内存检查等)查看/修改它,但他可以将其返回给您的班级,并且它可以恢复其状态。


2

使用私有内部类的原因之一可能是因为您使用的API需要从特定类继承,但是您不想将该类的知识导出给外部类的用户。例如:

// async_op.h -- someone else's api.
struct callback
{
    virtual ~callback(){}
    virtual void fire() = 0;
};

void do_my_async_op(callback *p);



// caller.cpp -- my api
class caller
{
private :
    struct caller_inner : callback
    {
        caller_inner(caller &self) : self_(self) {}
        void fire() { self_.do_callback(); }
        caller &self_;
    };

    void do_callback()
    {
        // Real callback
    }

    caller_inner inner_;

public :
    caller() : inner_(*this) {}

    void do_op()
    {
        do_my_async_op(&inner_);
    }
};

在这种情况下,do_my_async_op要求将回调类型的对象传递给它。此回调具有API使用的公共成员函数签名。

当从external_class调用do_op()时,它将使用私有内部类的实例,该实例从所需的回调类继承,而不是指向其自身的指针。外层类有一个对外层类的引用,其简单目的是将回调转移到外层类的私有 do_callback成员函数中。

这样做的好处是,您确保没有其他人可以调用公共的“ fire()”成员函数。



1

私有类和内部类用于提高封装级别并隐藏实现细节。

在C ++中,除了私有类外,通过实现类cpp的匿名名称空间也可以实现相同的概念。这很好地用于隐藏/私有化实现细节。

它与内部或私有类具有相同的想法,但是在更高的封装级别上,因为它在文件的编译单元外部是完全不可见的。它的任何内容都不会出现在头文件中,也不会在类的声明中向外显示。


关于-1有建设性的反馈吗?
hiwaylon 2012年

1
我没有投票,但是我想您并没有真正回答这个问题:您没有给出使用私有/内部类的任何理由。您只是在描述它的工作原理以及如何在c ++中做类似的事情
Simon Bergot,2012年

确实。我以为我和其他答案(隐藏实现细节,提高封装级别等)都是显而易见的,但我将尝试澄清一些问题。谢谢@西蒙。
hiwaylon 2012年

1
尼斯编辑。并且请不要因为这种反应而冒犯。SO网络正在尝试执行策略以提高信噪比。有些人对问题/答案的范围非常严格,有时却忘了保持友善(例如,评论自己的
下注

@Simon谢谢。令人失望的是,它表现出的通信质量很差。也许在网络匿名面纱后面“严格”一点有点容易?哦,好吧,我猜没有新内容。再次感谢您的积极反馈。
hiwaylon 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.