Click here to Skip to main content
15,886,362 members
Please Sign up or sign in to vote.
5.00/5 (13 votes)
See more:
Hello,

I'm just starting to learn about C#.

There is one thing that I just can't understand.
Why it's possible to do this?

I wonder why this doesn't generate a compiler error.
It seems I can declare a Class inside the same class, but that seems odd to me.
For example I wrote this small example to demonstrate.

I know it might not be good C# style yet but I'm only very beginning in programming.

class B
{
    int x;
    public B a;
    public B()
    {
        x=0;
        a = new B();
    }
}
class D
{
    public D()
    {
        B c = new B();
        B d = new B();
        //This can go on forever with these a properties!
        c.a.a.a.a.a.a.a.a.a.a.a.a.a = d;
    }
}



Although the code above is probably very badly written I just wanted to give an example of what I mean.
That whole c.a.a.a.a.a thing is weird and why is it even allowed?
Of course, I can't imagine ever using something like that, it would, I think, give an error if I declared a object of a class from within that same class definition.

Any insight would be great.

Thanks in advance.
Posted
Updated 9-Apr-11 3:06am
v3
Comments
Sergey Alexandrovich Kryukov 7-Apr-11 21:42pm    
Great example! How did you come to it? My 5.
--SA
Amar Kapadia 8-Apr-11 5:36am    
Good Morning SAKryukov...goodness..I just made it up a few minute before making my post! :) Thank you so much for explaining this...that was one fantastic response below!
Sergey Alexandrovich Kryukov 8-Apr-11 11:42am    
You're welcome!
Call again!
--SA
Albin Abel 8-Apr-11 1:59am    
Hi Amar, I think you got an acceptable answer from SAKryukov. The code won't work at runtime due to infinite recursion. Why it is allowable at compile time? because it could not decide. What if you return in the constructor before a = new B();. Compiler can't decide what you are going to do in a simple means. So let us consider this question solved? You can accept all the answers helped you.
Amar Kapadia 8-Apr-11 5:37am    
Thank you Albin. Yes, I've read everything this morning and I most certainly consider my question answered. As I am new to this forum I will now begin accepting (and thanking) all the posting to my question.

Very interesting example! Thank you very much.

First of all: there is no "class inside class" here. Not at all.
Class inside class is quite a normal construct called "nested class". I see nothing wrong with that. Do you want do discuss it? Not now, anyway.

What you have here is the instance if a class as a field of the same very class.
It's quite a valid example from the stand point of compiler — except one thing: let's remove "int x;" and "x = 0;" — these two lines only cause compiler warning and the variable is never used.

Now, the line "c.a.a.a.a.a.a.a.a.a.a.a.a.a = d;" really looks strange: it can really be continued by adding more and more ".a" to it. It looks like if there are infinite number of fields "a" referencing each other in infinite cycle. Note that at any given moment the length of this strange structure is finite. Still, it looks like a contradiction: there is no a piece of code which generate, say, a change of ten or 100 instances of the class "B". This contradiction is eliminated by the simple note: the line "c.a.a.a.a.a.a.a.a.a.a.a.a.a = d;" will never get to execution, because the initialization "c = new B();" above will never be finished, due to infinite recursion in constructor of the class "B".

Do you see, how interesting? It is not possible to reach this long line in principle, because it is based on the assumption of having the instance of the class "B" which can never be created.

Apparently a pathological case. Your question is: "why this doesn't generate a compiler error"? This is a great question! I know the answer. This is because the cases like that cannot be detected based on static analysis of the code. In general case, it is impossible to predict behavior of the program based on static analysis. By that reason, the compiler is not designed to analyze any cases. If a general case is undecidable, why trying to analyze any simplified special cases? So, you can easily create some code with infinite recursion or mutual recursion: it will compile, and the code will run into stack overflow exception.

The fact of undecidable computer program behavior is well-known. A famous halting problem is proven to algorithmically undecidable, see http://en.wikipedia.org/wiki/Halting_problem, see also Computability theory: Computability theory (computer science).

—SA
 
Share this answer
 
v4
Comments
Albin Abel 8-Apr-11 1:18am    
This is a great answer. The wikipedia link is a good one too. My 100+
Sergey Alexandrovich Kryukov 8-Apr-11 1:27am    
Thank you, Albin.
--SA
Sandeep Mewara 8-Apr-11 1:20am    
My 5++ SA!
Sergey Alexandrovich Kryukov 8-Apr-11 1:28am    
Thank you, Sandeep.
--SA
SwitcherSoft 8-Apr-11 2:44am    
OOPS, haha. Nice one
The other answers have addressed why the compiler doesn't complain about c.a.a.a....

I'd like to address why the ability to write self-referential classes is useful. There are many situations (kinds of data) in the world that have hierarchical data that looks similar from different levels. People at different levels of a management hierarchy come to mind:

public class Employee
    {
    public Employee Supervisor { get; set; }
    public IEnumerable<employee> Subordinates { get; private set; }
    }


Another common example is location. I currently work for a health-care organization. We have a location table that has:

Beds, that are in
Rooms, that are in
Units, that are in
Buildings, that are in
Facilities (i.e. hospitals).
(One could imagine this list extending to Neighborhood, City, County, State, Country, for some national health-care database).

All of these items are considered locations, so could be (and are commonly) implemented by means of a common class.

Using a loop, instead of the c.a.a.a.... syntax, to 'chain' a bunch of identical (nested) class references, might look like this:

public enum LocationType { Bed, Room, Unit, Building, Facility }
public class Location
    {
    public LocationType LocationType { get; set; }
    public Location ContainingLocation { get; set; }
    public IEnumerable<location> ContainedLocations { get; private set; }

    // Returns NULL, or the Facility at the top of the chain that contains 'this'.
    public Location GetContainingFacility()
        {
        Location parent = this.ContainingLocation;
        while (parent != null && parent.LocationType != LocationType.Facility)
            parent = parent.ContainingLocation;
        return parent;
        }
    }


If this method were called on a Location that had LocationType == LocationType.Bed, then the GetContainingFacility() method above would return the same as:

Location aBed = new Location();

Location facility = aBed    // bed
.ContainingLocation // room
.ContainingLocation // unit
.ContainingLocation // building
.ContainingLocation;    // facility


Naturally, whether you use c.a.a.a...., aBed.ContainingLocation...., or a loop, things can get confusing when a type contains a reference to another instance of that type. What really helps in this situation is to use field, property and method names that are as descriptive as possible (such as ContainingLocation).
 
Share this answer
 
v2
Comments
Amar Kapadia 9-Apr-11 6:22am    
Chris..thank you so much. I appreciate the time you took to write this out for me. Now I understand how in real world applications self-referential classes can be used. Your example certainly will require me to study them in detail but will do so to continue learning. Thank you again so much.
Dalek Dave 9-Apr-11 9:07am    
Good Answer.
It doesn't error out because it's perfectly legal for a class to contain an instance of itself, which can contain another instance, which can contain another, and another, ...

For example, a Linked List object can have a reference to another Linked List object, which can have a reference to another, and so on. If you started at the beginning of the list and wanted to reference, say, the 5th item in the list, it's conceivable to see code like like:
listedItem.nextItem.nextItem.nextItem.nextItem.Value = something
 
Share this answer
 
Comments
Sergey Alexandrovich Kryukov 7-Apr-11 21:45pm    
The pathological case is that this item can be no 5th or 6th, but any previously set ordinal number. The essence of this is that such line can never be reached due to infinite recursion.
Please see my Answer.
--SA
Amar Kapadia 8-Apr-11 5:45am    
Dave, thanks! Of course, a Linked List...completely forgot about those. Thank you for the explanation and answer.
Dalek Dave 9-Apr-11 9:07am    
Seems Reasonable.
Ok, your terminology is not accurate. Typically when people say class inside a class, they mean an inner class.

In your case you have a member field of the same type as the containing class. This is perfectly normal and is supported in most other similar languages too. If you've ever used a linked list, you'll see one of its most common and popular uses.
 
Share this answer
 
v2
Comments
Sergey Alexandrovich Kryukov 7-Apr-11 21:43pm    
Nishant, did you pay attention to the infinite recursion in the constructor? This is the key.
Please see my Answer.
--SA
Nish Nishant 7-Apr-11 21:45pm    
Ah I missed the recursion. Thanks.
Amar Kapadia 8-Apr-11 5:48am    
Good morning Nishant. Ahh...yes my terminology is wrong in my posting. Apologies. I will learn about inner class as well in time. Thank you for the answer.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900