开发者

What are reasons why one would want to use nested classes? [duplicate]

开发者 https://www.devze.com 2023-01-08 01:09 出处:网络
This question already has answers here: Why/when should you use nested classes in .net? Or shouldn't you?
This question already has answers here: Why/when should you use nested classes in .net? Or shouldn't you? (14 answers) Closed 9 years ago.

In this stackoverflow answer a commenter mentioned that "private nested classes" can be quite useful so I was reading about them in articles such as this one which tend to explain how nested classes function technically, but not why you would use them.

I suppose I would use private nested classes for little helper classes that belong to a larger class, but often I will need a helper class from another class and so I would just have to take the extra effort to (1) make the nested class non-nested or (2) make it开发者_如何学运维 public and then access it with the outer-class prefix on it, which both seems to be extra work without any added-value for having the nested class in the first place. Hence in general I really don't see a use case for nested classes, other than perhaps to keep classes a bit more organized into groups, but I that also goes against the one-class-per-file clarity that I have come to enjoy.

In what ways do you use nested classes to make your code more manageable, readable, efficient?


You've answered your own question. Use nested classes when you need a helper class that is meaningless outside the class; particularly when the nested class can make use of private implementation details of the outer class.

Your argument that nested classes are useless is also an argument that private methods are useless: a private method might be useful outside of the class, and therefore you'd have to make it internal. An internal method might be useful outside of the assembly, and therefore you'd make it public. Therefore all methods should be public. If you think that's a bad argument, then what is different about you making the same argument for classes instead of methods?

I make nested classes all the time because I am frequently in the position of needed to encapsulate functionality in a helper that makes no sense outside of the class, and can use private implementation details of the outer class. For example, I write compilers. I recently wrote a class SemanticAnalyzer that does semantic analysis of parse trees. One of its nested classes is LocalScopeBuilder. Under what circumstances would I need to build a local scope when I am not analyzing the semantics of a parse tree? Never. That class is entirely an implementation detail of the semantic analyzer. I plan to add more nested classes with names like NullableArithmeticAnalyzer and OverloadResolutionAnalyzer that are also not useful outside of the class, but I want to encapsulate rules of the language in those specific classes.

People also use nested classes to build things like iterators, or comparators - things that make no sense outside of the class and are exposed via a well-known interface.

A pattern I use quite frequently is to have private nested classes that extend their outer class:

abstract public class BankAccount
{
    private BankAccount() { }
    // Now no one else can extend BankAccount because a derived class
    // must be able to call a constructor, but all the constructors are
    // private!
    private sealed class ChequingAccount : BankAccount { ... }
    public static BankAccount MakeChequingAccount() { return new ChequingAccount(); }
    private sealed class SavingsAccount : BankAccount { ... }

and so on. Nested classes work very well with the factory pattern. Here BankAccount is a factory for various types of bank account, all of which can use the private implementation details of BankAccount. But no third party can make their own type EvilBankAccount that extends BankAccount.


Returning an interface to the caller whose implementation you want to hide.

public class Outer
{
    private class Inner : IEnumerable<Foo>
    {
        /* Presumably this class contains some functionality which Outer needs
         * to access, but which shouldn't be visible to callers 
         */
    }

    public IEnumerable<Foo> GetFoos()
    {
        return new Inner();
    }
}


Private helper classes is a good example.

For instance, state objects for background threads. There is no compelling reason to expose those types. Defining them as private nested types seems a quite clean way to handle the case.


I use them when two bound values (like in a hash table) are not enough internally, but are enough externally. Then i create a nested class with the properties i need to store, and expose only a few of them through methods.

I think this makes sense, because if no one else is going to use it, why create an external class for it? It just doesn't make sense to.

As for one class per file, you can create partial classes with the partial keyword, which is what I usually do.


One compelling example I've run into recently is the Node class of many data structures. A Quadtree, for example, needs to know how it stores the data in its nodes, but no other part of your code should care.


I've found a few cases where they've been quite handy:

  1. Management of complex private state, such as an InterpolationTriangle used by an Interpolator class. The user of the Interpolator doesn't need to know that it's implemented using Delauney triangulation and certainly doesn't need to know about the triangles, so the data structure is a private nested class.

  2. As others have mentioned, you can expose data used by the class with an interface without revealing the full implementation of a class. Nested classes can also access private state of the outer class, which allows you to write tightly coupled code without exposing that tight coupling publicly (or even internally to the rest of the assembly).

  3. I've run into a few cases where a framework expects a class to derive from some base class (such as DependencyObject in WPF), but you want your class to inherit from a different base. It's possible to inter-operate with the framework by using a private nested class that descends from the framework base class. Because the nested class can access private state (you just pass it the parent's 'this' when you create it), you can basically use this to implement a poor man's multiple inheritance via composition.


I think others have covered the use cases for public and private nested classes well.

One point I haven't seen made was an answer your concern about one-class-per-file. You can solve this by making the outer class partial, and move the inner class definition to a separate file.

OuterClass.cs:

namespace MyNameSpace
{
    public partial class OuterClass
    {
        // main class members here
        // can use inner class
    }
}

OuterClass.Inner.cs:

namespace MyNameSpace
{
    public partial class OuterClass
    {
        private class Inner
        {
            // inner class members here
        }
    }
}

You could even make use of Visual Studio's item nesting to make OuterClass.Inner.cs a 'child' of OuterClass.cs, to avoid cluttering your solution explorer.


One very common pattern where this technique is used is in scenarios where a class returns an interface or base class type from one of its properties or methods, but the concrete type is a private nested class. Consider the following example.

public class MyCollection : IEnumerable
{
  public IEnumerator GetEnumerator()
  {
    return new MyEnumerator();
  }

  private class MyEnumerator
  {
  }
}


I usually do it when I need a combination of SRP (Single Responsibility Principal) in certain situations.

"Well, if SRP is your goal, why not split them into different classes?" You will do this 80% of the time, but what about situations where the classes you create are useless to the outside world? You don't want classes that only you will use to clutter your assembly's API.

"Well, isn't that what internal is for?" Sure. For about 80% of these cases. But what about internal classes who must access or modify the state of public classes? For example, that class which was broken up into one or more internal classes to satisfy your SRP streak? You would have to mark all the methods and properties for use by these internal classes as internal as well.

"What's wrong with that?" Nothing. For about 80% of these cases. Of course, now you're cluttering the internal interface of your classes with methods/properties that are only of use to those classes which you created earlier. And now you have to worry about other people on your team writing internal code won't mess up your state by using those methods in ways that you hadn't expected.

Internal classes get to modify the state of any instance of the type in which they are defined. So, without adding members to the definition of your type, your internal classes can work on them as needed. Which, in about 14 cases in 100, will be your best bet to keep your types clean, your code reliable/maintainable, and your responsibilities singular.


They are really nice for, as an example, an implementation of the singleton pattern.

I have a couple of places where I am using them to "add" value, as well. I have a multi-select combobox where my internal class stores the state of the checkbox and the data item as well. no need for the world to know about/use this internal class.


Private anonymous nested classes are essential for event handlers in the GUI.

If some class is not part of the API another class exports, it must be made private. Otherwise you are exposing more than you intend. The "million dollar bug" was an example of this. Most programmers are too slack about this.

Peter


The question is tagged C# so I'm not sure this is of interest, but in COM you can use inner classes to implement interfaces when a class C++ implements multiple COM interfaces... essentially you use it for composition rather than multiple-inheritance.

Additionally in MFC and perhaps other technologies you might need your control/dialog to have a drop-target class, which makes little sense other than as a nested class.


If it is necessary for an object to return some abstract information about its state, a private nested class may be suitable. For example, if an Fnord supports "save context" and "restore context" methods, it may be useful to have the "save context" function return an object of type Fnord.SavedContext. Type access rules aren't always the most helpful; for example, it seems difficult to allow Fnord to access properties and methods of a Fnord.SavedContext without making such properties and methods visible to outsiders. On the other hand, one could have Fnord.CreateSaveContext simply create a New Fnord.SaveContext with the Fnord as a parameter (since Fnord.SaveContext can access the internals of Fnord), and Fnord.LoadContextFrom() can call Fnord.SaveContext.RestoreContextTo().

0

精彩评论

暂无评论...
验证码 换一张
取 消