79

I am relatively new to C# and each time I begin to work on a C# project (I only worked on nearly mature projects in C#) I wonder why there are no inner classes?

Maybe I don't understand their goal. To me, inner classes -- at least private inner classes -- look a lot like "inner procedures" in Pascal / Modula-2 / Ada : they allow to break down a main class in smaller parts in order to ease the understanding.

Example : here is what is see most of the time :

public class ClassA
{
   public MethodA()
   {
      <some code>
      myObjectClassB.DoSomething(); // ClassB is only used by ClassA
      <some code>
   }
}

public class ClassB
{
   public DoSomething()
   {
   }
}

Since ClassB will be used (at least for a while) only by ClassA, my guess is that this code would be better expressed as follow :

   public class ClassA
   {
      public MethodA()
      {
         <some code>
         myObjectClassB.DoSomething(); // Class B is only usable by ClassA
         <some code>
      }

      private class ClassB
      {
         public DoSomething()
         {
         }
      }
   }

I would be glad to hear from you on this subject - Am I right?

johnnyRose
  • 7,310
  • 17
  • 40
  • 61
Sylvain Rodrigue
  • 4,751
  • 5
  • 53
  • 67

4 Answers4

82

Nested classes (probably best to avoid the word "inner" as nested classes in C# are somewhat different to inner classes in Java) can indeed be very useful.

One pattern which hasn't been mentioned is the "better enum" pattern - which can be even more flexible than the one in Java:

public abstract class MyCleverEnum
{
    public static readonly MyCleverEnum First = new FirstCleverEnum();
    public static readonly MyCleverEnum Second = new SecondCleverEnum();

    // Can only be called by this type *and nested types*
    private MyCleverEnum()
    {
    }

    public abstract void SomeMethod();
    public abstract void AnotherMethod();

    private class FirstCleverEnum : MyCleverEnum
    {
        public override void SomeMethod()
        {
             // First-specific behaviour here
        }

        public override void AnotherMethod()
        {
             // First-specific behaviour here
        }
    }

    private class SecondCleverEnum : MyCleverEnum
    {
        public override void SomeMethod()
        {
             // Second-specific behaviour here
        }

        public override void AnotherMethod()
        {
             // Second-specific behaviour here
        }
    }
}

We could do with some language support to do some of this automatically - and there are lots of options I haven't shown here, like not actually using a nested class for all of the values, or using the same nested class for multiple values, but giving them different constructor parameters. But basically, the fact that the nested class can call the private constructor gives a lot of power.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 12
    Very clever! But I will never use it. Software book writers and their readers (!) are often clever peoples. They think to a lot of bright things and it's cool. But software maintainers are ordinary people who didn't read Gamma/Skeet. My guest: the more it is clever, the less it is maintenable... – Sylvain Rodrigue Jan 18 '09 at 00:22
  • 30
    That's true until it becomes a recognised pattern. It's like "while ((line = streamReader.ReadLine()) != null)" looks bad to start with - side-effects in a while condition! But when it's idiomatic, you get past that smell and appreciate the conciseness. – Jon Skeet Jan 18 '09 at 00:31
  • I have posted something about inner classes implementing the outer class here : http://www.developpezvous.com/2008/06/21/c-comment-creer-un-wrapper-proprement. It is based on .NET's Collection class. Reading again your answer, and finding very clever, I think that there are a lot possibilities there – Sylvain Rodrigue Jan 18 '09 at 00:53
  • 2
    @Sylvain: I think it's like LINQ - it *adds* to the maintainability when you're used to it, but looks odd at first sight. But I agree that it's not a common pattern in C# yet :( – Jon Skeet Jan 18 '09 at 07:58
  • @Jon, (1) What is the advantage of FirstCleverEnum and SecondCleverEnum being embedded classes? (2) Why do these classes contain "enum"? The outer class seems to be an enum, but the inner ones don't. (3) Not that I would want to, but why can't I do `MyCleverEnum.First.First.First.SomeMethod()`? BTW, if you Google "better enum pattern", you get one hit: this SO page :) – devuxer Nov 12 '09 at 20:53
  • 1
    1) They have access to the private constructor of the containing class. You can guarantee they are the *only* classes which will create instances of the outer class - and only the outer class can see them to create instances of them. 2) They're members of the enum, effectively. 3) Because you can't reference static members as if they were instance members (unlike in Java). This is a good thing :) – Jon Skeet Nov 12 '09 at 21:33
  • 3
    @Jon, thanks! I think I got it now. `MyCleverEnum` acts as both the the holder of the enum members and the interface of each enum member, so it is more succinct than other solutions. The use of embedded classes ensures that the support classes are hidden from outside the class. The private constructor makes the class behave like a static/singleton class from outside the class but like an abstract base class from inside the class. – devuxer Nov 12 '09 at 22:11
  • Bravo. I can really see how this pattern could be fitted to maintain our existing code. Enumerations are used all over the place -- as keys for domain object properties that you'd want to query on. This means every time you want different behaviour depending on the enum value there's a huge awful switch statement rather than something more graceful using polymorphism. Almost all enum-value-dependent behaviour should be converted to type-dependent behaviour of some kind. – Lisa Jun 02 '11 at 06:12
  • 1
    One thing I've found icky with using class scoping like that is that the public inner classes end up having "dotted" public names. Is there any clean way to have a publicly-accessible class have the same scope as a nested class, but be publicly accessible via a "simple' name? – supercat Sep 26 '11 at 22:21
  • @supercat: No, I don't believe so. – Jon Skeet Sep 26 '11 at 22:25
  • 2
    @supercat, You could put a using directive at the top of the file `using FirstCleverEnum = MyCleverEnum.FirstCleverEnum`. This can mitigate the problem, but there is no way to "export" this alias. – Marty Neal Jul 27 '12 at 23:21
  • That was a great idea to get an elegant solution for the 'better enum'. The only missing piece is a switch statement as smart as the current code above. – dmihailescu Mar 29 '13 at 04:25
  • yeah, that enum is pretty vlever, but i don't see its use yet. may be someone give some good example to launch my brain into thinking? – jungle_mole Dec 13 '15 at 18:22
  • @JonSkeet How does your solution play with EF Core and the new value converters? I've got a java enum where each enum value has a text representatio, e.g. `ERROR("Feil"), INIT("Starter");` etc, which I would like to convert to C#. I could just store the enums as strings (in C#/EF Core) and then use a `translator`class of course, but it would be nice to know if it is possible to use your solution and map to and from the DB using EF Core. – norgie Apr 18 '18 at 15:20
  • @norgie: I don't know enough about EF Core to know whether it's even possible, I'm afraid. – Jon Skeet Apr 18 '18 at 16:02
  • @JonSkeet Ok. I notice that the Java enums can have a public constructor which, if available in your solution, would make it fairly trivial to implement an EF Core value converter to convert to/from the value stored in the database. See: https://learn.microsoft.com/en-us/ef/core/modeling/value-conversions – norgie Apr 19 '18 at 07:36
  • @JonSkeet I decided to try something like this: builder.Property(fid => fid.Status).IsRequired().HasConversion(new ValueConverter(v => v.ToString(), s => JobStatus.FromString(s))); – norgie Apr 19 '18 at 08:04
  • @JonSkeet what difference does public/private on members of private nested calss make? For eg. lets say another method `ThirdMethod` inside `private class FirstCleverEnum`? – Shishir Gupta May 03 '18 at 09:04
  • @Shishir: If it's private, it can only be called from within that class (and any further nested classes) – Jon Skeet May 03 '18 at 12:04
31

The Framework Design Guidelines has the best rules for using nested classes that I have found to date.

Here's a brief summary list:

  1. Do use nested types when the relationship between type and nested type is such the member-accessibility semantics are desired.

  2. Do NOT use public nested types as a logical group construct

  3. Avoid using publicly exposed nested types.

  4. Do NOT use nested types if the type is likely to be referenced outside of the containing type.

  5. Do NOT use nested types if they need to be instantiated by client code.

  6. Do NOT define a nested type as a member of an interface.

Kevin Panko
  • 8,356
  • 19
  • 50
  • 61
matt_dev
  • 5,176
  • 5
  • 33
  • 44
  • 2
    some of these are fairly obvious (such as 4 and 5). But could you point at reasons why the other rules are recommended? – Peter Bagnall Sep 15 '14 at 15:35
  • @PeterBagnall is it just me, or 6th contradicts to the accepted answer? – jungle_mole Dec 13 '15 at 18:25
  • 1
    @jungle_mole a nested type implementing a public interface is fine and common. The nested types type should never be exposed publicly. – Erick Nov 08 '16 at 14:04
  • @Erick yep. i'd just swapped two parts of two "designs" in my head. tbh reasons for this 6th are obvious. what i was even thinking – jungle_mole Feb 13 '17 at 20:34
12

You should limit the responsibilities of each class so that each one stays simple, testable and reusable. Private inner classes go against that. They contribute to the complexity of the outer class, they are not testable and they are not reusable.

Wim Coenen
  • 66,094
  • 13
  • 157
  • 251
  • Simple, testable and reusable are very great. But what about encapsulation? When something should not be seen outside, why make it public? In my book, inner classes can make the whole thing somewhat simpler. And they are testables (with relexion) ! – Sylvain Rodrigue Jan 18 '09 at 00:09
  • 4
    I would pick encapsulation as an argument **against** inner classes. Inner classes can access private members of their outer classes! – Wim Coenen Jan 18 '09 at 00:28
  • Your right ! Having an inner class add some sort of coupling between it and the outer class - I did not see it at first - sorry. And thanks ! – Sylvain Rodrigue Jan 18 '09 at 01:50
  • 10
    I see programmers use data structures like tuples all the time instead of private classes. I would definitely argue that a private class leads to code that is easier to read and less prone to error. Arguing that it violates encapsulation is not correct. There's nothing about a private class that violates encapsulation. It's contained inside of a class and should and does have all the rights of any other member of the outer class. – Mark Aug 13 '14 at 17:42
  • As with everything, it depends. And in this case I think it depends on the specifics of the nested class. For example, if you create a dto within a parent class (e.g. to keep method signatures clean), then there's nothing to test. It isn't any different than creating private Tuples imo, just more clear. But if you create a rich nested class that have functionality, then you're going to have a bad time. – Sinaesthetic Aug 23 '16 at 00:18
  • I agree with Mark. I have used tuples a lot, for things that should be encapsulated in outer class. Later on, i switch to private classes. They are much more convenient than a tuples. They can have a behavior, and meaningful properties, instead of Item1, Item2... . And they don't bloat outside code. – vllado2 Jan 11 '17 at 14:52
3

For me personally I only create private inner classes if I need to create in-process collections of an object that may require methods on them.

Otherwise, it could cause confusion for other developers working on the project to actually find these classes, as they are not very clear as to where they are.

Tom Anderson
  • 10,807
  • 3
  • 46
  • 63
  • 1
    But since they are privates classes, other developers should not be aware of them, unless, of course, they have to maintain the outer class. Of course, this can be a problem where people are not aware of private inner classes. Thanks for your answer ! – Sylvain Rodrigue Jan 17 '09 at 22:57
  • 16
    If that confused my developers I would get new developers. – DancesWithBamboo Jan 18 '09 at 04:27