开发者

C#, is it okay to use nested classes for logical structure?

开发者 https://www.devze.com 2023-04-04 18:24 出处:网络
I am having a bit of a debate about the use of nested classes. The situation is that a class name makes sense to be repeated in two or more places, and while there is moderate similarity between each

I am having a bit of a debate about the use of nested classes. The situation is that a class name makes sense to be repeated in two or more places, and while there is moderate similarity between each of the different instances, they are generally different. The nested classes are not often (if at all) needed beyond the scope of their parent class.

So then, rather than just coming up with three different class names, this seems to make more sense to me.

class A {
   class B {
}

class M {
   class B {
   }
}

class Q {
   class B {
   }
}

The obvious problem with that is not functionality, but rather consistency/repetition. I w开发者_开发知识库as wondering if other developers have ever struggled with the same thing, and what some of the opinions were.


The .net Design Guide advises against it:

  • "Do not use public nested types as a logical grouping construct; use namespaces for this."
  • "Avoid publicly exposed nested types. The only exception to this is when variables of the nested type need to be declared in rare scenarios such as subclassing or other advanced customization scenarios."

That's also what the base class library does: In the System.Web.UI namespace, you have DataGridItem, DataListItem, ListViewItem, MenuItem, RepeaterItem, etc. All of these could be called Item and nested inside DataGrid, DataList, etc. However, this would violate the two principles outlined above.


It looks okay when your classes are small. Once they get bloated, you really start thinking about moving them in separate files.

More to your point, if you want to use both A.B and M.B in the same code you have to always type A.B and M.B, which can be a pain.


If class B has any similarities between each inner class instance, would it make sense for you to abstract the similarities of B to a base class that exists alongside A, M, and Q? (I think so.) Then your inner classes, while they may have the same name, would be a little cleaner.

With that said, this type of structure can be seen for things like Metadata in an MVC application. In that instance you'd have something like:

[MetadataType(typeof(A.Metadata))]
class A
{
    protected class Metadata
    {
        ...
    }
}

[MetadataType(typeof(B.Metadata))]
class B
{
    protected class Metadata
    {
        ...
    }
}

In these case the inner classes each serve the same purpose but their implementations vary with each parent class. Also, with the Metadata definitions here, it makes a lot of sense to keep a class that helps describe its parent as an inner class. If there's any chance you might want to re-use the inner classes elsewhere then I would move them outside of their parents.

I think it's a little atypical to see this in practice otherwise. I'm sure there are good examples, but I bet there are more bad examples of this type of pattern.


I would say it is sometimes ok, but usually not a good design, to use private nested classes. I once refactored an existing very large class in my project to give it private nested classes. The reason why I did this was that some methods took dozens of parameters and this gave them a more logical grouping. In this sense I see nested classes as a good quick fix. It made sense because no one outside that class had any use for any of those fields.

Generally, I would shy away from using nested classes in an initial design - and think twice before considering them in a redesign. In maintenance, if you have the time, it is better to redesign the whole class and split them out into separate classes in separate files that are internal.

I think this strategy is also better for testability than using nested classes is. Due to greater dependencies with the outer class and other classes in the application, my refactored nested classes weren't much easier to unit test than the original large class that passed around many parameters. If you split nested classes so that they are on their own, you can write more discrete unit tests that actually test units rather than, effectively, combining the unit tests for the outer class and the inner class. This will give you more confidence in saying, "Yes, the inner class works at the unit test level" and "Yes, the outer class works at the unit test level" (which also tests how it fits together with the inner class, e.g. in computing formulas).


I understand your sample is sort of contrived. Still, if your class names are similar enough - or identical - you really shouldn't make them nested classes. As a general rule you should shy away from using nested classes at all.

If I'm remembering correctly, the .NET Framework Guidelines recommends against using nested classes as well. Nested Type Usage Guidelines is a little old (back to version 1.1), but the principles still apply.

Do not use nested types if the following are true:

  • The type must be instantiated by client code. If a type has a public constructor, it probably should not be nested. The rationale behind this guideline is that if a nested type can be instantiated, it indicates that the type has a place in the library on its own. You can create it, use it, and destroy it without using the outer type. Therefore, it should not be nested. An inner type should not be widely reused outside of the outer type without a relationship to the outer type.
  • References to the type are commonly declared in client code.


Well you can use namespaces to do things like this too (just create a new folder in VS). Which is better for organising and will pretty much give you the same result.

But if the subclass is only relevant to the parent class then I don't see the harm in it.

Then again, if you are calling them the same thing I would guess they do a similar drop and you may want to look into abstraction, perhaps your parent classes could be done differently too. Really depends on what you need them to do though


I like doing that, for me it makes the use more clearer and especially finding names less of a problem. But usally i try to limit this on private classes or public enums. For example

class Text {
   enum Alignment

class UIElement {
   enum Alignment

or

class Quadtree {
   private class Node

class Octree {
   private class Node


Don't create a nested class if there is any chance (or business reason) that you'll have to use it in some other place (use namespace instead and dot not hesitate to create class with long name if you need to).

For instance I use nested class for DTO between my controller and my view, or in a class to represent a cache entry.


If you want to name them the same but have different types you could use different namespaces.

Namespace1.MyClass{}
Namespace2.MyClass{}

This will end up with two different types despite the classes being named the same.


It really depends on the functionality of the nested class. That is very similar to the way the C++ STL defined iterator differently in each class. There's nothing wrong with the practice, per se, as long as the concept of each is truly different based on the encompassing class.

It can be, somewhat, a matter of style and taste, but personally I don't see an issue as long as they are truly different and dependent on the definition of the encapsulating class. It does tend to get more confusing, though, if they are publicly visible outside the class. Thus, I would not personally expose the classes publicly.


There's nothing inherently wrong about nested classes, as long as you stick to the following rules of thumb:

  1. Never public or internal. There are special cases, such as when you're using a nested class to implement IEnumerator. But even then, the class itself should be kept private, since instances of it are being returned as IEnumerator, and it's really just being done as a way to avoid junking up the namespace with classes that aren't supposed to be instantiated.

  2. Keep them small. A private nested class that's really just used for storing and passing around data in a more organized way is fine, and can sometimes be a very useful tool. (Not entirely unlike how anonymous classes are useful.) But if you're looking to use them to package up large chunks of functionality, it becomes a code smell that suggests you might want to consider refactoring the outer class instead.

0

精彩评论

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