C
C#3mo ago
jdsus

Use cases of nested classes

If I have an outer class and an inner class. Most of the time it’s for helper purposes and the inner class is instantiated within the outer class correct? I’m a little stuck on this but, it wouldn’t make sense to instantiate an instance of the outer class from within the inner class, if that’s even possible? Also does the inner class have access to the outer class’ methods?
27 Replies
Angius
Angius3mo ago
Usually, nested classes are used when they're needed only somewhere in the parent class For example,
class FooHandler(DbContext ctx)
{
public Task<FooResult> Handle(int id)
{
var thing = await ctx
.Where(t => t.Id == id)
.Select(t => new FooResult(t.Name, t.Count, t.Status))
.FirstOrDefaultAsync();
return thing ?? throw new NotFoundException();
}

record FooResult(string Name, int Count, string Status);
}
class FooHandler(DbContext ctx)
{
public Task<FooResult> Handle(int id)
{
var thing = await ctx
.Where(t => t.Id == id)
.Select(t => new FooResult(t.Name, t.Count, t.Status))
.FirstOrDefaultAsync();
return thing ?? throw new NotFoundException();
}

record FooResult(string Name, int Count, string Status);
}
(record is just a class under the hood, I used it here for brevity, works the same with classes) Here's a real-life usage example. Both Query and Result are declared as nested classes, because they're only ever used within the parent class Also, a fun trick to categorize some global consts and stuff
public static class Config
{
public static class User
{
public const int MaxNameLength = 60;
}


public static class Blogpost
{
public const int MaxTitleLength = 60;
public const int MinBodyLength = 30_000_000;
public static readonly string[] AllowedImageTypes = ["png", "jpg", "gif"];
}
}
public static class Config
{
public static class User
{
public const int MaxNameLength = 60;
}


public static class Blogpost
{
public const int MaxTitleLength = 60;
public const int MinBodyLength = 30_000_000;
public static readonly string[] AllowedImageTypes = ["png", "jpg", "gif"];
}
}
lets you access it with Config.Blogpost.MaxTitleLength
Jimmacle
Jimmacle3mo ago
the only real difference an inner class and not-inner class have is accessibility instances of the inner and outer classes aren't related in any special way
jcotton42
jcotton423mo ago
notably, nested classes can be private, only accessible by their outer class
jdsus
jdsusOP3mo ago
Oh, it makes sense if you only use nested classes as helper classes I was just confused on if people use it for other reasons or not This can also just all be done with non nested classes right Do people typically pass the outer class into the inner class? Thats what trips me up
Jimmacle
Jimmacle3mo ago
you have to if you want to do something with an instance of the outer class inside the inner class, there is no magic relationship between an instance of an outer and inner class as far as the runtime is concerned nested classes aren't even a concept, they're just 2 different classes
jcotton42
jcotton423mo ago
afaik pretty much only the JVM langs do this implicit relationship
SleepWellPupper
SleepWellPupper3mo ago
Note though that nested types inherit the generic parameters of their containing type. i.e.:
class Foo<T>
{
public class Bar
{
public static Type Type => typeof(T);
}
}
class Foo<T>
{
public class Bar
{
public static Type Type => typeof(T);
}
}
VoidPointer
VoidPointer3mo ago
My main use case, and pretty much the only one I use regularly, is for constants. Our solution has hundreds of guid config keys, and to get a nice hierarchical name like MyMainService.SubServiceGroup.SubServiceUrlId, we nest the static classes with the consts:
namespace SystemConfigs;
public static class MyMainService
{
public const Guid MainServiceBaseUrl = new Guid("3a2b2bbb-a219-4004-8aea-db36c0e1b822");
public static class SubServiceGroup
{
public const Guid SubServiceUrlId = new Guid("3a2b2bbb-a219-4004-8aea-db36c0e1b822");
}
}
namespace SystemConfigs;
public static class MyMainService
{
public const Guid MainServiceBaseUrl = new Guid("3a2b2bbb-a219-4004-8aea-db36c0e1b822");
public static class SubServiceGroup
{
public const Guid SubServiceUrlId = new Guid("3a2b2bbb-a219-4004-8aea-db36c0e1b822");
}
}
jdsus
jdsusOP3mo ago
This isn’t possible by just making two disconnected classes? Ie. class A{} classB{ public B(A obj){} }
Jimmacle
Jimmacle3mo ago
i'm saying it's effectively the same as that
jdsus
jdsusOP3mo ago
Ohhh Ok I see now The only benefit is that it’s grouped like would be A.B compared to split like how I sent it But either way if I have multiple nested classes they’re compiled as split
Jimmacle
Jimmacle3mo ago
right, nested classes are just a syntax thing that lets them access private members of the outer class if they have an instance of it besides that they're exactly the same as any other class i rarely expose nested classes outside the class they're defined in, for me it's usually classes that only serve a purpose in implementing behavior of the outer class
jdsus
jdsusOP3mo ago
Ok it makes sense since private members can only be accessed from within and the class would be nested inside But it has to be passed from within the class methods?
Jimmacle
Jimmacle3mo ago
right, nested classes don't automatically get a reference to the class they're defined in
jdsus
jdsusOP3mo ago
So if I have: class A{ public SomeFunction(){ B instanceOne= new B(this); } class B{ A passedRef; public B(A instanceNum){ passedRef= instanceNum; } } } VS A someInstance = new A(); A.B passedInstance = new A.B(someInstance) Are they the same thing? Sorry about formatting idk how it’s done here LOL
Jimmacle
Jimmacle3mo ago
B instanceOne(this); isn't valid syntax in C# you have to explicitly new the instance but those are both passing an instance of A into B, so they're the same thing in that respect
jdsus
jdsusOP3mo ago
Oh…. That’s awkward I thought I saw that syntax somewhere
Jimmacle
Jimmacle3mo ago
C++
jdsus
jdsusOP3mo ago
So I’d need to do B instanceOne(new A()) What’s the point of doing this within the outer class? It’d make a new instance instead of using the existing instance. There’s no way to pass the current instance?
Jimmacle
Jimmacle3mo ago
there is, the this keyword is a reference to the instance of the current class the invalid part was the initialization syntax, not the this
jdsus
jdsusOP3mo ago
Oh wait sorry you’d do B instanceOne = new B(this) That would be correct?
Jimmacle
Jimmacle3mo ago
yeah
jdsus
jdsusOP3mo ago
So if I have: class A{ public SomeFunction(){ B instanceOne= new B(this); } class B{ A passedRef; public B(A instanceNum){ passedRef= instanceNum; } } } A example = new A() example.SomeFunction VS A someInstance = new A(); A.B passedInstance = new A.B(someInstance) Are they the same thing?
Jimmacle
Jimmacle3mo ago
are both examples creating an instance of B/A.B and passing a reference to an instance of A?
jdsus
jdsusOP3mo ago
Yep but I was just wondering if there is any difference in the underlying concept like whether or not the second example can access the private members? Looks like they should work the same
Jimmacle
Jimmacle3mo ago
that is purely based on whether the class is defined inside the other or not it doesn't matter how you instantiate it or get it a reference to the outer class if it's nested inside, it can access the private members of the class it's nested in
jdsus
jdsusOP3mo ago
In both examples they’re defined within each other no? It’s just that one has me instantiating the inner within the class definition and the other is instantiating it in my main Makes sense, thank you for helping me understand It’s adding up now!

Did you find this page helpful?