Why/when should you use nested classes in .net? Or shouldn't you?
Asked Answered
X

14

107

In Kathleen Dollard's 2008 blog post, she presents an interesting reason to use nested classes in .net. However, she also mentions that FxCop doesn't like nested classes. I'm assuming that the people writing FxCop rules aren't stupid, so there must be reasoning behind that position, but I haven't been able to find it.

Xavler answered 7/9, 2008 at 23:1 Comment(2)
Wayback Archive link to the blog post: web.archive.org/web/20141127115939/https://blogs.msmvps.com/…Cheadle
As nawfal points out, our buddy Eric Lippert answered a duplicate of this question here, the answer in question starting with, "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..."Cryptoclastic
A
110

Use a nested class when the class you are nesting is only useful to the enclosing class. For instance, nested classes allow you to write something like (simplified):

public class SortedMap {
    private class TreeNode {
        TreeNode left;
        TreeNode right;
    }
}

You can make a complete definition of your class in one place, you don't have to jump through any PIMPL hoops to define how your class works, and the outside world doesn't need to see anything of your implementation.

If the TreeNode class was external, you would either have to make all the fields public or make a bunch of get/set methods to use it. The outside world would have another class polluting their intellisense.

Amathiste answered 7/9, 2008 at 23:12 Comment(2)
To add to this: You can also use partial classes in seperate files to manage your code a bit better. Put the inner class in a seperate file (SortedMap.TreeNode.cs in this case). This should keep your code clean, while also keeping your code seperated :)Ferriage
There will be cases where you will need to make the nested class public or internal if it is being used in return type of a public API or a public property of the container class. I'm not sure if it is a good practice though. Such cases might make more sense to pull out the nested class outside the container class. System.Windows.Forms.ListViewItem.ListViewSubItem class in .Net framework is one such example.Puke
L
17

From Sun's Java Tutorial:

Why Use Nested Classes? There are several compelling reasons for using nested classes, among them:

  • It is a way of logically grouping classes that are only used in one place.
  • It increases encapsulation.
  • Nested classes can lead to more readable and maintainable code.

Logical grouping of classes—If a class is useful to only one other class, then it is logical to embed it in that class and keep the two together. Nesting such "helper classes" makes their package more streamlined.

Increased encapsulation—Consider two top-level classes, A and B, where B needs access to members of A that would otherwise be declared private. By hiding class B within class A, A's members can be declared private and B can access them. In addition, B itself can be hidden from the outside world. <- This doesn't apply to C#'s implementation of nested classes, this only applies to Java.

More readable, maintainable code—Nesting small classes within top-level classes places the code closer to where it is used.

Lotte answered 7/9, 2008 at 23:56 Comment(4)
This doesn't really apply, as you cannot access instance variables from the enclosing class in C# like you can in Java. Only static members are accessible.Incurve
However, if you pass an instance of the enclosing class into the nested class, the nested class has full access to all members through that instance variable... so really, it's like Java makes the instance variable implicit, whereas in C# you have to make it explicit.Pinnule
@Pinnule No it's not, in Java the nested class actually captures the parent class instance when instantiated - among other things this means it prevents the parent from being garbage collected. It also means the nested class cannot be instantiated without parent class. So no, these are not the same at all.Unwritten
@TomášZato My description is pretty apt, really. There's effectively an implicit parent instance variable in nested classes in Java, while in C#, you have to explicitly hand the inner class the instance. A consequence of this, as you say, is that Java's inner classes must have a parent instance, while C#'s don't. My main point in any case was that C#'s inner classes can also access to its parents private fields and properties, but that it has to be passed the parent instance explicitly to be able do it.Pinnule
H
10

Fully Lazy and thread-safe singleton pattern

public sealed class Singleton
{
    Singleton()
    {
    }

    public static Singleton Instance
    {
        get
        {
            return Nested.instance;
        }
    }
    
    class Nested
    {
        // Explicit static constructor to tell C# compiler
        // not to mark type as beforefieldinit
        static Nested()
        {
        }

        internal static readonly Singleton instance = new Singleton();
    }
}

source: https://csharpindepth.com/Articles/Singleton

Hardesty answered 5/7, 2009 at 18:32 Comment(0)
N
5

It depends on the usage. I rarely would ever use a Public nested class but use Private nested classes all of the time. A private nested class can be used for a sub-object that is intended to be used only inside the parent. An example of this would be if a HashTable class contains a private Entry object to store data internally only.

If the class is meant to be used by the caller (externally), I generally like making it a separate standalone class.

Nonfulfillment answered 7/9, 2008 at 23:39 Comment(0)
C
5

In addition to the other reasons listed above, there is one more reason that I can think of not only to use nested classes, but in fact public nested classes. For those who work with multiple generic classes that share the same generic type parameters, the ability to declare a generic namespace would be extremely useful. Unfortunately, .Net (or at least C#) does not support the idea of generic namespaces. So in order to accomplish the same goal, we can use generic classes to fulfill the same goal. Take the following example classes related to a logical entity:

public  class       BaseDataObject
                    <
                        tDataObject, 
                        tDataObjectList, 
                        tBusiness, 
                        tDataAccess
                    >
        where       tDataObject     : BaseDataObject<tDataObject, tDataObjectList, tBusiness, tDataAccess>
        where       tDataObjectList : BaseDataObjectList<tDataObject, tDataObjectList, tBusiness, tDataAccess>, new()
        where       tBusiness       : IBaseBusiness<tDataObject, tDataObjectList, tBusiness, tDataAccess>
        where       tDataAccess     : IBaseDataAccess<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{
}

public  class       BaseDataObjectList
                    <
                        tDataObject, 
                        tDataObjectList, 
                        tBusiness, 
                        tDataAccess
                    >
:   
                    CollectionBase<tDataObject>
        where       tDataObject     : BaseDataObject<tDataObject, tDataObjectList, tBusiness, tDataAccess>
        where       tDataObjectList : BaseDataObjectList<tDataObject, tDataObjectList, tBusiness, tDataAccess>, new()
        where       tBusiness       : IBaseBusiness<tDataObject, tDataObjectList, tBusiness, tDataAccess>
        where       tDataAccess     : IBaseDataAccess<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{
}

public  interface   IBaseBusiness
                    <
                        tDataObject, 
                        tDataObjectList, 
                        tBusiness, 
                        tDataAccess
                    >
        where       tDataObject     : BaseDataObject<tDataObject, tDataObjectList, tBusiness, tDataAccess>
        where       tDataObjectList : BaseDataObjectList<tDataObject, tDataObjectList, tBusiness, tDataAccess>, new()
        where       tBusiness       : IBaseBusiness<tDataObject, tDataObjectList, tBusiness, tDataAccess>
        where       tDataAccess     : IBaseDataAccess<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{
}

public  interface   IBaseDataAccess
                    <
                        tDataObject, 
                        tDataObjectList, 
                        tBusiness, 
                        tDataAccess
                    >
        where       tDataObject     : BaseDataObject<tDataObject, tDataObjectList, tBusiness, tDataAccess>
        where       tDataObjectList : BaseDataObjectList<tDataObject, tDataObjectList, tBusiness, tDataAccess>, new()
        where       tBusiness       : IBaseBusiness<tDataObject, tDataObjectList, tBusiness, tDataAccess>
        where       tDataAccess     : IBaseDataAccess<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{
}

We can simplify the signatures of these classes by using a generic namespace (implemented via nested classes):

public
partial class   Entity
                <
                    tDataObject, 
                    tDataObjectList, 
                    tBusiness, 
                    tDataAccess
                >
        where   tDataObject     : Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>.BaseDataObject
        where   tDataObjectList : Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>.BaseDataObjectList, new()
        where   tBusiness       : Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>.IBaseBusiness
        where   tDataAccess     : Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>.IBaseDataAccess
{

    public  class       BaseDataObject {}

    public  class       BaseDataObjectList : CollectionBase<tDataObject> {}

    public  interface   IBaseBusiness {}

    public  interface   IBaseDataAccess {}

}

Then, through the use of partial classes as suggested by Erik van Brakel in an earlier comment, you can separate the classes into separate nested files. I recommend using a Visual Studio extension like NestIn to support nesting the partial class files. This allows the "namespace" class files to also be used to organize the nested class files in a folder like way.

For example:

Entity.cs

public
partial class   Entity
                <
                    tDataObject, 
                    tDataObjectList, 
                    tBusiness, 
                    tDataAccess
                >
        where   tDataObject     : Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>.BaseDataObject
        where   tDataObjectList : Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>.BaseDataObjectList, new()
        where   tBusiness       : Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>.IBaseBusiness
        where   tDataAccess     : Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>.IBaseDataAccess
{
}

Entity.BaseDataObject.cs

partial class   Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{

    public  class   BaseDataObject
    {

        public  DataTimeOffset  CreatedDateTime     { get; set; }
        public  Guid            CreatedById         { get; set; }
        public  Guid            Id                  { get; set; }
        public  DataTimeOffset  LastUpdateDateTime  { get; set; }
        public  Guid            LastUpdatedById     { get; set; }

        public
        static
        implicit    operator    tDataObjectList(DataObject dataObject)
        {
            var returnList  = new tDataObjectList();
            returnList.Add((tDataObject) this);
            return returnList;
        }

    }

}

Entity.BaseDataObjectList.cs

partial class   Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{

    public  class   BaseDataObjectList : CollectionBase<tDataObject>
    {

        public  tDataObjectList ShallowClone() 
        {
            var returnList  = new tDataObjectList();
            returnList.AddRange(this);
            return returnList;
        }

    }

}

Entity.IBaseBusiness.cs

partial class   Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{

    public  interface   IBaseBusiness
    {
        tDataObjectList Load();
        void            Delete();
        void            Save(tDataObjectList data);
    }

}

Entity.IBaseDataAccess.cs

partial class   Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{

    public  interface   IBaseDataAccess
    {
        tDataObjectList Load();
        void            Delete();
        void            Save(tDataObjectList data);
    }

}

The files in the visual studio solution explorer would then be organized as such:

Entity.cs
+   Entity.BaseDataObject.cs
+   Entity.BaseDataObjectList.cs
+   Entity.IBaseBusiness.cs
+   Entity.IBaseDataAccess.cs

And you would implement the generic namespace like the following:

User.cs

public
partial class   User
:
                Entity
                <
                    User.DataObject, 
                    User.DataObjectList, 
                    User.IBusiness, 
                    User.IDataAccess
                >
{
}

User.DataObject.cs

partial class   User
{

    public  class   DataObject : BaseDataObject 
    {
        public  string  UserName            { get; set; }
        public  byte[]  PasswordHash        { get; set; }
        public  bool    AccountIsEnabled    { get; set; }
    }

}

User.DataObjectList.cs

partial class   User
{

    public  class   DataObjectList : BaseDataObjectList {}

}

User.IBusiness.cs

partial class   User
{

    public  interface   IBusiness : IBaseBusiness {}

}

User.IDataAccess.cs

partial class   User
{

    public  interface   IDataAccess : IBaseDataAccess {}

}

And the files would be organized in the solution explorer as follows:

User.cs
+   User.DataObject.cs
+   User.DataObjectList.cs
+   User.IBusiness.cs
+   User.IDataAccess.cs

The above is a simple example of using an outer class as a generic namespace. I've built "generic namespaces" containing 9 or more type parameters in the past. Having to keep those type parameters synchronized across the nine types that all needed to know the type parameters was tedious, especially when adding a new parameter. The use of generic namespaces makes that code far more manageable and readable.

Caddaric answered 31/3, 2015 at 22:6 Comment(0)
S
3

If I understand Katheleen's article right, she proposes to use nested class to be able to write SomeEntity.Collection instead of EntityCollection< SomeEntity>. In my opinion it's controversial way to save you some typing. I'm pretty sure that in real world application collections will have some difference in implementations, so you will need to create separate class anyway. I think that using class name to limit other class scope is not a good idea. It pollutes intellisense and strengthen dependencies between classes. Using namespaces is a standard way to control classes scope. However I find that usage of nested classes like in @hazzen comment is acceptable unless you have tons of nested classes which is a sign of bad design.

Successor answered 7/9, 2008 at 23:24 Comment(0)
T
1

I often use nested classes to hide implementation detail. An example from Eric Lippert's answer here:

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 { ... }
}

This pattern becomes even better with use of generics. See this question for two cool examples. So I end up writing

Equality<Person>.CreateComparer(p => p.Id);

instead of

new EqualityComparer<Person, int>(p => p.Id);

Also I can have a generic list of Equality<Person> but not EqualityComparer<Person, int>

var l = new List<Equality<Person>> 
        { 
         Equality<Person>.CreateComparer(p => p.Id),
         Equality<Person>.CreateComparer(p => p.Name) 
        }

where as

var l = new List<EqualityComparer<Person, ??>>> 
        { 
         new EqualityComparer<Person, int>>(p => p.Id),
         new EqualityComparer<Person, string>>(p => p.Name) 
        }

is not possible. That's the benefit of nested class inheriting from parent class.

Another case (of the same nature - hiding implementation) is when you want to make a class's members (fields, properties etc) accessible only for a single class:

public class Outer 
{
   class Inner //private class
   {
       public int Field; //public field
   }

   static inner = new Inner { Field = -1 }; // Field is accessible here, but in no other class
}
Torritorricelli answered 18/4, 2013 at 21:7 Comment(0)
T
1

Another use not yet mentioned for nested classes is the segregation of generic types. For example, suppose one wants to have some generic families of static classes that can take methods with various numbers of parameters, along with values for some of those parameters, and generate delegates with fewer parameters. For example, one wishes to have a static method which can take an Action<string, int, double> and yield a String<string, int> which will call the supplied action passing 3.5 as the double; one may also wish to have a static method which can take an an Action<string, int, double> and yield an Action<string>, passing 7 as the int and 5.3 as the double. Using generic nested classes, one can arrange to have the method invocations be something like:

MakeDelegate<string,int>.WithParams<double>(theDelegate, 3.5);
MakeDelegate<string>.WithParams<int,double>(theDelegate, 7, 5.3);

or, because the latter types in each expression can be inferred even though the former ones can't:

MakeDelegate<string,int>.WithParams(theDelegate, 3.5);
MakeDelegate<string>.WithParams(theDelegate, 7, 5.3);

Using the nested generic types makes it possible to tell which delegates are applicable to which parts of the overall type description.

Tetrapod answered 2/9, 2013 at 19:27 Comment(0)
H
1

The nested classes can be used for following needs:

  1. Classification of the data
  2. When the logic of the main class is complicated and you feel like you require subordinate objects to manage the class
  3. When you that the state and existence of the class fully depends on the enclosing class
Hardware answered 27/4, 2014 at 12:51 Comment(0)
D
0

As nawfal mentioned implementation of Abstract Factory pattern, that code can be axtended to achieve Class Clusters pattern which is based on Abstract Factory pattern.

Dealer answered 31/10, 2013 at 8:10 Comment(0)
L
0

I like to nest exceptions that are unique to a single class, ie. ones that are never thrown from any other place.

For example:

public class MyClass
{
    void DoStuff()
    {
        if (!someArbitraryCondition)
        {
            // This is the only class from which OhNoException is thrown
            throw new OhNoException(
                "Oh no! Some arbitrary condition was not satisfied!");
        }
        // Do other stuff
    }

    public class OhNoException : Exception
    {
        // Constructors calling base()
    }
}

This helps keep your project files tidy and not full of a hundred stubby little exception classes.

Ln answered 19/12, 2014 at 23:41 Comment(0)
B
0

Bear in mind that you'll need to test the nested class. If it is private, you won't be able to test it in isolation.

You could make it internal, though, in conjunction with the InternalsVisibleTo attribute. However, this would be the same as making a private field internal only for testing purposes, which I consider bad self-documentation.

So, you may want to only implement private nested classes involving low complexity.

Blumenfeld answered 10/3, 2015 at 9:3 Comment(0)
P
0

yes for this case:

class Join_Operator
{

    class Departamento
    {
        public int idDepto { get; set; }
        public string nombreDepto { get; set; }
    }

    class Empleado
    {
        public int idDepto { get; set; }
        public string nombreEmpleado { get; set; }
    }

    public void JoinTables()
    {
        List<Departamento> departamentos = new List<Departamento>();
        departamentos.Add(new Departamento { idDepto = 1, nombreDepto = "Arquitectura" });
        departamentos.Add(new Departamento { idDepto = 2, nombreDepto = "Programación" });

        List<Empleado> empleados = new List<Empleado>();
        empleados.Add(new Empleado { idDepto = 1, nombreEmpleado = "John Doe." });
        empleados.Add(new Empleado { idDepto = 2, nombreEmpleado = "Jim Bell" });

        var joinList = (from e in empleados
                        join d in departamentos on
                        e.idDepto equals d.idDepto
                        select new
                        {
                            nombreEmpleado = e.nombreEmpleado,
                            nombreDepto = d.nombreDepto
                        });
        foreach (var dato in joinList)
        {
            Console.WriteLine("{0} es empleado del departamento de {1}", dato.nombreEmpleado, dato.nombreDepto);
        }
    }
}
Polson answered 23/6, 2018 at 2:16 Comment(1)
Why? Add some context to the code in your solution to help future readers understand the reasoning behind your answer.Shouse
J
0

Base on my understanding of this concept we could use this feature when classes are related to each other conceptually. I mean some of them are complete one Item in our business like entities that exist in the DDD world that help to an aggregate root object to complete its business logic.

In order to clarify I'm going to show this via an example:

Imagine that we have two classes like Order and OrderItem. In order class, we are going to manage all orderItems and in OrderItem we are holding data about a single order for clarification, you can see below classes:

 class Order
    {
        private List<OrderItem> _orderItems = new List<OrderItem>();

        public void AddOrderItem(OrderItem line)
        {
            _orderItems.Add(line);
        }

        public double OrderTotal()
        {
            double total = 0;
            foreach (OrderItem item in _orderItems)
            {
                total += item.TotalPrice();
            }

            return total;
        }

        // Nested class
        public class OrderItem
        {
            public int ProductId { get; set; }
            public int Quantity { get; set; }
            public double Price { get; set; }
            public double TotalPrice() => Price * Quantity;
        }
    }

    class Program
    {

        static void Main(string[] args)
        {
            Order order = new Order();

            Order.OrderItem orderItem1 = new Order.OrderItem();
            orderItem1.ProductId = 1;
            orderItem1.Quantity = 5;
            orderItem1.Price = 1.99;
            order.AddOrderItem(orderItem1);

            Order.OrderItem orderItem2 = new Order.OrderItem();
            orderItem2.ProductId = 2;
            orderItem2.Quantity = 12;
            orderItem2.Price = 0.35;
            order.AddOrderItem(orderItem2);

            Console.WriteLine(order.OrderTotal());
            ReadLine();
        }


    }
Jacquerie answered 3/8, 2020 at 5:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.